Browse code

vendor: bump deps

bump runc/libcontainer to v0.0.5
bump runc deps to latest
bump docker/libnetwork to 04cc1fa0a89f8c407b7be8cab883d4b17531ea7d

Signed-off-by: Antonio Murdaca <runcom@redhat.com>

Antonio Murdaca authored on 2015/11/26 08:44:44
Showing 69 changed files
... ...
@@ -22,7 +22,7 @@ clone git github.com/vdemeester/shakers 3c10293ce22b900c27acad7b28656196fcc2f73b
22 22
 clone git golang.org/x/net 47990a1ba55743e6ef1affd3a14e5bac8553615d https://github.com/golang/net.git
23 23
 
24 24
 #get libnetwork packages
25
-clone git github.com/docker/libnetwork b4ddf18317b19d6e4bcc821145589749206a7d00
25
+clone git github.com/docker/libnetwork 04cc1fa0a89f8c407b7be8cab883d4b17531ea7d
26 26
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
27 27
 clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
28 28
 clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
... ...
@@ -49,14 +49,14 @@ clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf
49 49
 clone git github.com/jfrazelle/go v1.5.1-1
50 50
 clone git github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
51 51
 
52
-# this runc commit from branch relabel_fix_docker_1.9.1, pls remove it when you
53
-# update next time
54
-clone git github.com/opencontainers/runc 1349b37bd56f4f5ce2690b5b2c0f53f88a261c67 # libcontainer
52
+clone git github.com/opencontainers/runc v0.0.5 # libcontainer
55 53
 # libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json)
56 54
 clone git github.com/coreos/go-systemd v4
57
-clone git github.com/godbus/dbus v2
58
-clone git github.com/syndtr/gocapability 66ef2aa7a23ba682594e2b6f74cf40c0692b49fb
59
-clone git github.com/golang/protobuf 655cdfa588ea
55
+clone git github.com/godbus/dbus v3
56
+clone git github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
57
+clone git github.com/golang/protobuf f7137ae6b19afbfd61a94b746fda3b3fe0491874
58
+
59
+# gelf logging driver deps
60 60
 clone git github.com/Graylog2/go-gelf 6c62a85f1d47a67f2a5144c0e745b325889a8120
61 61
 
62 62
 clone git github.com/fluent/fluent-logger-golang v1.0.0
... ...
@@ -64,11 +64,11 @@ func PathBusEscape(path string) string {
64 64
 type Conn struct {
65 65
 	// sysconn/sysobj are only used to call dbus methods
66 66
 	sysconn *dbus.Conn
67
-	sysobj  *dbus.Object
67
+	sysobj  dbus.BusObject
68 68
 
69 69
 	// sigconn/sigobj are only used to receive dbus signals
70 70
 	sigconn *dbus.Conn
71
-	sigobj  *dbus.Object
71
+	sigobj  dbus.BusObject
72 72
 
73 73
 	jobListener struct {
74 74
 		jobs map[dbus.ObjectPath]chan<- string
... ...
@@ -86,14 +86,30 @@ type Conn struct {
86 86
 // New establishes a connection to the system bus and authenticates.
87 87
 // Callers should call Close() when done with the connection.
88 88
 func New() (*Conn, error) {
89
-	return newConnection(dbus.SystemBusPrivate)
89
+	return newConnection(func() (*dbus.Conn, error) {
90
+		return dbusAuthHelloConnection(dbus.SystemBusPrivate)
91
+	})
90 92
 }
91 93
 
92 94
 // NewUserConnection establishes a connection to the session bus and
93 95
 // authenticates. This can be used to connect to systemd user instances.
94 96
 // Callers should call Close() when done with the connection.
95 97
 func NewUserConnection() (*Conn, error) {
96
-	return newConnection(dbus.SessionBusPrivate)
98
+	return newConnection(func() (*dbus.Conn, error) {
99
+		return dbusAuthHelloConnection(dbus.SessionBusPrivate)
100
+	})
101
+}
102
+
103
+// NewSystemdConnection establishes a private, direct connection to systemd.
104
+// This can be used for communicating with systemd without a dbus daemon.
105
+// Callers should call Close() when done with the connection.
106
+func NewSystemdConnection() (*Conn, error) {
107
+	return newConnection(func() (*dbus.Conn, error) {
108
+		// We skip Hello when talking directly to systemd.
109
+		return dbusAuthConnection(func() (*dbus.Conn, error) {
110
+			return dbus.Dial("unix:path=/run/systemd/private")
111
+		})
112
+	})
97 113
 }
98 114
 
99 115
 // Close closes an established connection
... ...
@@ -103,12 +119,12 @@ func (c *Conn) Close() {
103 103
 }
104 104
 
105 105
 func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
106
-	sysconn, err := dbusConnection(createBus)
106
+	sysconn, err := createBus()
107 107
 	if err != nil {
108 108
 		return nil, err
109 109
 	}
110 110
 
111
-	sigconn, err := dbusConnection(createBus)
111
+	sigconn, err := createBus()
112 112
 	if err != nil {
113 113
 		sysconn.Close()
114 114
 		return nil, err
... ...
@@ -132,7 +148,7 @@ func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
132 132
 	return c, nil
133 133
 }
134 134
 
135
-func dbusConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
135
+func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
136 136
 	conn, err := createBus()
137 137
 	if err != nil {
138 138
 		return nil, err
... ...
@@ -149,8 +165,16 @@ func dbusConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
149 149
 		return nil, err
150 150
 	}
151 151
 
152
-	err = conn.Hello()
152
+	return conn, nil
153
+}
154
+
155
+func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
156
+	conn, err := dbusAuthConnection(createBus)
153 157
 	if err != nil {
158
+		return nil, err
159
+	}
160
+
161
+	if err = conn.Hello(); err != nil {
154 162
 		conn.Close()
155 163
 		return nil, err
156 164
 	}
... ...
@@ -158,6 +182,6 @@ func dbusConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
158 158
 	return conn, nil
159 159
 }
160 160
 
161
-func systemdObject(conn *dbus.Conn) *dbus.Object {
161
+func systemdObject(conn *dbus.Conn) dbus.BusObject {
162 162
 	return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
163 163
 }
164 164
new file mode 100644
... ...
@@ -0,0 +1,178 @@
0
+// Copyright 2015 CoreOS, Inc.
1
+//
2
+// Licensed under the Apache License, Version 2.0 (the "License");
3
+// you may not use this file except in compliance with the License.
4
+// You may obtain a copy of the License at
5
+//
6
+//     http://www.apache.org/licenses/LICENSE-2.0
7
+//
8
+// Unless required by applicable law or agreed to in writing, software
9
+// distributed under the License is distributed on an "AS IS" BASIS,
10
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+// See the License for the specific language governing permissions and
12
+// limitations under the License.
13
+
14
+// Package journal provides write bindings to the local systemd journal.
15
+// It is implemented in pure Go and connects to the journal directly over its
16
+// unix socket.
17
+//
18
+// To read from the journal, see the "sdjournal" package, which wraps the
19
+// sd-journal a C API.
20
+//
21
+// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html
22
+package journal
23
+
24
+import (
25
+	"bytes"
26
+	"encoding/binary"
27
+	"errors"
28
+	"fmt"
29
+	"io"
30
+	"io/ioutil"
31
+	"net"
32
+	"os"
33
+	"strconv"
34
+	"strings"
35
+	"syscall"
36
+)
37
+
38
+// Priority of a journal message
39
+type Priority int
40
+
41
+const (
42
+	PriEmerg Priority = iota
43
+	PriAlert
44
+	PriCrit
45
+	PriErr
46
+	PriWarning
47
+	PriNotice
48
+	PriInfo
49
+	PriDebug
50
+)
51
+
52
+var conn net.Conn
53
+
54
+func init() {
55
+	var err error
56
+	conn, err = net.Dial("unixgram", "/run/systemd/journal/socket")
57
+	if err != nil {
58
+		conn = nil
59
+	}
60
+}
61
+
62
+// Enabled returns true if the local systemd journal is available for logging
63
+func Enabled() bool {
64
+	return conn != nil
65
+}
66
+
67
+// Send a message to the local systemd journal. vars is a map of journald
68
+// fields to values.  Fields must be composed of uppercase letters, numbers,
69
+// and underscores, but must not start with an underscore. Within these
70
+// restrictions, any arbitrary field name may be used.  Some names have special
71
+// significance: see the journalctl documentation
72
+// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
73
+// for more details.  vars may be nil.
74
+func Send(message string, priority Priority, vars map[string]string) error {
75
+	if conn == nil {
76
+		return journalError("could not connect to journald socket")
77
+	}
78
+
79
+	data := new(bytes.Buffer)
80
+	appendVariable(data, "PRIORITY", strconv.Itoa(int(priority)))
81
+	appendVariable(data, "MESSAGE", message)
82
+	for k, v := range vars {
83
+		appendVariable(data, k, v)
84
+	}
85
+
86
+	_, err := io.Copy(conn, data)
87
+	if err != nil && isSocketSpaceError(err) {
88
+		file, err := tempFd()
89
+		if err != nil {
90
+			return journalError(err.Error())
91
+		}
92
+		_, err = io.Copy(file, data)
93
+		if err != nil {
94
+			return journalError(err.Error())
95
+		}
96
+
97
+		rights := syscall.UnixRights(int(file.Fd()))
98
+
99
+		/* this connection should always be a UnixConn, but better safe than sorry */
100
+		unixConn, ok := conn.(*net.UnixConn)
101
+		if !ok {
102
+			return journalError("can't send file through non-Unix connection")
103
+		}
104
+		unixConn.WriteMsgUnix([]byte{}, rights, nil)
105
+	} else if err != nil {
106
+		return journalError(err.Error())
107
+	}
108
+	return nil
109
+}
110
+
111
+// Print prints a message to the local systemd journal using Send().
112
+func Print(priority Priority, format string, a ...interface{}) error {
113
+	return Send(fmt.Sprintf(format, a...), priority, nil)
114
+}
115
+
116
+func appendVariable(w io.Writer, name, value string) {
117
+	if !validVarName(name) {
118
+		journalError("variable name contains invalid character, ignoring")
119
+	}
120
+	if strings.ContainsRune(value, '\n') {
121
+		/* When the value contains a newline, we write:
122
+		 * - the variable name, followed by a newline
123
+		 * - the size (in 64bit little endian format)
124
+		 * - the data, followed by a newline
125
+		 */
126
+		fmt.Fprintln(w, name)
127
+		binary.Write(w, binary.LittleEndian, uint64(len(value)))
128
+		fmt.Fprintln(w, value)
129
+	} else {
130
+		/* just write the variable and value all on one line */
131
+		fmt.Fprintf(w, "%s=%s\n", name, value)
132
+	}
133
+}
134
+
135
+func validVarName(name string) bool {
136
+	/* The variable name must be in uppercase and consist only of characters,
137
+	 * numbers and underscores, and may not begin with an underscore. (from the docs)
138
+	 */
139
+
140
+	valid := name[0] != '_'
141
+	for _, c := range name {
142
+		valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
143
+	}
144
+	return valid
145
+}
146
+
147
+func isSocketSpaceError(err error) bool {
148
+	opErr, ok := err.(*net.OpError)
149
+	if !ok {
150
+		return false
151
+	}
152
+
153
+	sysErr, ok := opErr.Err.(syscall.Errno)
154
+	if !ok {
155
+		return false
156
+	}
157
+
158
+	return sysErr == syscall.EMSGSIZE || sysErr == syscall.ENOBUFS
159
+}
160
+
161
+func tempFd() (*os.File, error) {
162
+	file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX")
163
+	if err != nil {
164
+		return nil, err
165
+	}
166
+	syscall.Unlink(file.Name())
167
+	if err != nil {
168
+		return nil, err
169
+	}
170
+	return file, nil
171
+}
172
+
173
+func journalError(s string) error {
174
+	s = "journal error: " + s
175
+	fmt.Fprintln(os.Stderr, s)
176
+	return errors.New(s)
177
+}
0 178
deleted file mode 100644
... ...
@@ -1,166 +0,0 @@
1
-// Copyright 2015 CoreOS, Inc.
2
-//
3
-// Licensed under the Apache License, Version 2.0 (the "License");
4
-// you may not use this file except in compliance with the License.
5
-// You may obtain a copy of the License at
6
-//
7
-//     http://www.apache.org/licenses/LICENSE-2.0
8
-//
9
-// Unless required by applicable law or agreed to in writing, software
10
-// distributed under the License is distributed on an "AS IS" BASIS,
11
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
-// See the License for the specific language governing permissions and
13
-// limitations under the License.
14
-
15
-// Package journal provides write bindings to the systemd journal
16
-package journal
17
-
18
-import (
19
-	"bytes"
20
-	"encoding/binary"
21
-	"errors"
22
-	"fmt"
23
-	"io"
24
-	"io/ioutil"
25
-	"net"
26
-	"os"
27
-	"strconv"
28
-	"strings"
29
-	"syscall"
30
-)
31
-
32
-// Priority of a journal message
33
-type Priority int
34
-
35
-const (
36
-	PriEmerg Priority = iota
37
-	PriAlert
38
-	PriCrit
39
-	PriErr
40
-	PriWarning
41
-	PriNotice
42
-	PriInfo
43
-	PriDebug
44
-)
45
-
46
-var conn net.Conn
47
-
48
-func init() {
49
-	var err error
50
-	conn, err = net.Dial("unixgram", "/run/systemd/journal/socket")
51
-	if err != nil {
52
-		conn = nil
53
-	}
54
-}
55
-
56
-// Enabled returns true iff the systemd journal is available for logging
57
-func Enabled() bool {
58
-	return conn != nil
59
-}
60
-
61
-// Send a message to the systemd journal. vars is a map of journald fields to
62
-// values.  Fields must be composed of uppercase letters, numbers, and
63
-// underscores, but must not start with an underscore. Within these
64
-// restrictions, any arbitrary field name may be used.  Some names have special
65
-// significance: see the journalctl documentation
66
-// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
67
-// for more details.  vars may be nil.
68
-func Send(message string, priority Priority, vars map[string]string) error {
69
-	if conn == nil {
70
-		return journalError("could not connect to journald socket")
71
-	}
72
-
73
-	data := new(bytes.Buffer)
74
-	appendVariable(data, "PRIORITY", strconv.Itoa(int(priority)))
75
-	appendVariable(data, "MESSAGE", message)
76
-	for k, v := range vars {
77
-		appendVariable(data, k, v)
78
-	}
79
-
80
-	_, err := io.Copy(conn, data)
81
-	if err != nil && isSocketSpaceError(err) {
82
-		file, err := tempFd()
83
-		if err != nil {
84
-			return journalError(err.Error())
85
-		}
86
-		_, err = io.Copy(file, data)
87
-		if err != nil {
88
-			return journalError(err.Error())
89
-		}
90
-
91
-		rights := syscall.UnixRights(int(file.Fd()))
92
-
93
-		/* this connection should always be a UnixConn, but better safe than sorry */
94
-		unixConn, ok := conn.(*net.UnixConn)
95
-		if !ok {
96
-			return journalError("can't send file through non-Unix connection")
97
-		}
98
-		unixConn.WriteMsgUnix([]byte{}, rights, nil)
99
-	} else if err != nil {
100
-		return journalError(err.Error())
101
-	}
102
-	return nil
103
-}
104
-
105
-func appendVariable(w io.Writer, name, value string) {
106
-	if !validVarName(name) {
107
-		journalError("variable name contains invalid character, ignoring")
108
-	}
109
-	if strings.ContainsRune(value, '\n') {
110
-		/* When the value contains a newline, we write:
111
-		 * - the variable name, followed by a newline
112
-		 * - the size (in 64bit little endian format)
113
-		 * - the data, followed by a newline
114
-		 */
115
-		fmt.Fprintln(w, name)
116
-		binary.Write(w, binary.LittleEndian, uint64(len(value)))
117
-		fmt.Fprintln(w, value)
118
-	} else {
119
-		/* just write the variable and value all on one line */
120
-		fmt.Fprintf(w, "%s=%s\n", name, value)
121
-	}
122
-}
123
-
124
-func validVarName(name string) bool {
125
-	/* The variable name must be in uppercase and consist only of characters,
126
-	 * numbers and underscores, and may not begin with an underscore. (from the docs)
127
-	 */
128
-
129
-	valid := name[0] != '_'
130
-	for _, c := range name {
131
-		valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
132
-	}
133
-	return valid
134
-}
135
-
136
-func isSocketSpaceError(err error) bool {
137
-	opErr, ok := err.(*net.OpError)
138
-	if !ok {
139
-		return false
140
-	}
141
-
142
-	sysErr, ok := opErr.Err.(syscall.Errno)
143
-	if !ok {
144
-		return false
145
-	}
146
-
147
-	return sysErr == syscall.EMSGSIZE || sysErr == syscall.ENOBUFS
148
-}
149
-
150
-func tempFd() (*os.File, error) {
151
-	file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX")
152
-	if err != nil {
153
-		return nil, err
154
-	}
155
-	syscall.Unlink(file.Name())
156
-	if err != nil {
157
-		return nil, err
158
-	}
159
-	return file, nil
160
-}
161
-
162
-func journalError(s string) error {
163
-	s = "journal error: " + s
164
-	fmt.Fprintln(os.Stderr, s)
165
-	return errors.New(s)
166
-}
167 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+*
... ...
@@ -2,6 +2,7 @@
2 2
 *.o
3 3
 *.a
4 4
 *.so
5
+bin/
5 6
 
6 7
 # Folders
7 8
 integration-tmp/
... ...
@@ -33,4 +34,4 @@ cmd/dnet/dnet
33 33
 .project
34 34
 .settings/
35 35
 
36
-libnetwork-build.created
36
+libnetworkbuild.created
37 37
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+FROM golang:1.4-cross
1
+RUN apt-get update && apt-get -y install iptables
2
+RUN go get github.com/tools/godep \
3
+		github.com/golang/lint/golint \
4
+		golang.org/x/tools/cmd/vet \
5
+		golang.org/x/tools/cmd/goimports \
6
+		golang.org/x/tools/cmd/cover\
7
+		github.com/mattn/goveralls
... ...
@@ -1,42 +1,46 @@
1
-.PHONY: all all-local build build-local check check-code check-format run-tests check-local integration-tests install-deps coveralls circle-ci start-services clean
1
+.PHONY: all all-local build build-local clean cross cross-local check check-code check-format run-tests integration-tests check-local coveralls circle-ci-cross circle-ci-build circle-ci-check circle-ci
2 2
 SHELL=/bin/bash
3 3
 build_image=libnetworkbuild
4 4
 dockerargs = --privileged -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork
5 5
 container_env = -e "INSIDECONTAINER=-incontainer=true"
6
-docker = docker run --rm -it ${dockerargs} ${container_env} ${build_image}
6
+docker = docker run --rm -it ${dockerargs} $$EXTRA_ARGS ${container_env} ${build_image}
7 7
 ciargs = -e "COVERALLS_TOKEN=$$COVERALLS_TOKEN" -e "INSIDECONTAINER=-incontainer=true"
8
-cidocker = docker run ${ciargs} ${dockerargs} golang:1.4
9
-
10
-all: ${build_image}.created build check integration-tests clean
11
-
12
-integration-tests: ./cmd/dnet/dnet
13
-	@./test/integration/dnet/run-integration-tests.sh
14
-
15
-./cmd/dnet/dnet:
16
-	make build
17
-
18
-clean:
19
-	@if [ -e ./cmd/dnet/dnet ]; then \
20
-		echo "Removing dnet binary"; \
21
-		rm -rf ./cmd/dnet/dnet; \
22
-	fi
23
-
24
-all-local: check-local build-local
8
+cidocker = docker run ${dockerargs} ${ciargs} ${container_env} ${build_image}
9
+CROSS_PLATFORMS = linux/amd64 linux/386 linux/arm windows/amd64 windows/386
25 10
 
26 11
 ${build_image}.created:
27
-	docker run --name=libnetworkbuild -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork golang:1.4 make install-deps
28
-	docker commit libnetworkbuild ${build_image}
29
-	docker rm libnetworkbuild
12
+	docker build -f Dockerfile.build -t ${build_image} .
30 13
 	touch ${build_image}.created
31 14
 
15
+all: ${build_image}.created build check integration-tests clean
16
+
17
+all-local: build-local check-local integration-tests-local clean
18
+
32 19
 build: ${build_image}.created
33 20
 	@echo "Building code... "
34 21
 	@${docker} ./wrapmake.sh build-local
35 22
 	@echo "Done building code"
36 23
 
37 24
 build-local:
38
-	@$(shell which godep) go build  ./...
39
-	@$(shell which godep) go build -o ./cmd/dnet/dnet ./cmd/dnet
25
+	@mkdir -p "bin"
26
+	$(shell which godep) go build -o "bin/dnet" ./cmd/dnet
27
+
28
+clean:
29
+	@if [ -d bin ]; then \
30
+		echo "Removing dnet binaries"; \
31
+		rm -rf bin; \
32
+	fi
33
+
34
+cross: ${build_image}.created
35
+	@mkdir -p "bin"
36
+	@for platform in ${CROSS_PLATFORMS}; do \
37
+	        EXTRA_ARGS="-e GOOS=$${platform%/*} -e GOARCH=$${platform##*/}" ; \
38
+		echo "$${platform}..." ; \
39
+	        ${docker} make cross-local ; \
40
+	done
41
+
42
+cross-local:
43
+	$(shell which godep) go build -o "bin/dnet-$$GOOS-$$GOARCH" ./cmd/dnet
40 44
 
41 45
 check: ${build_image}.created
42 46
 	@${docker} ./wrapmake.sh check-local
... ...
@@ -71,27 +75,31 @@ run-tests:
71 71
 	done
72 72
 	@echo "Done running tests"
73 73
 
74
-check-local:	check-format check-code start-services run-tests
74
+check-local:	check-format check-code run-tests
75
+
76
+integration-tests: ./bin/dnet
77
+	@./test/integration/dnet/run-integration-tests.sh
75 78
 
76
-install-deps:
77
-	apt-get update && apt-get -y install iptables zookeeperd
78
-	git clone https://github.com/golang/tools /go/src/golang.org/x/tools
79
-	go install golang.org/x/tools/cmd/vet
80
-	go install golang.org/x/tools/cmd/goimports
81
-	go install golang.org/x/tools/cmd/cover
82
-	go get github.com/tools/godep
83
-	go get github.com/golang/lint/golint
84
-	go get github.com/mattn/goveralls
79
+./bin/dnet:
80
+	make build
85 81
 
86 82
 coveralls:
87 83
 	-@goveralls -service circleci -coverprofile=coverage.coverprofile -repotoken $$COVERALLS_TOKEN
88 84
 
89 85
 # CircleCI's Docker fails when cleaning up using the --rm flag
90
-# The following target is a workaround for this
86
+# The following targets are a workaround for this
87
+circle-ci-cross: ${build_image}.created
88
+	@mkdir -p "bin"
89
+	@for platform in ${CROSS_PLATFORMS}; do \
90
+	        EXTRA_ARGS="-e GOOS=$${platform%/*} -e GOARCH=$${platform##*/}" ; \
91
+		echo "$${platform}..." ; \
92
+	        ${cidocker} make cross-local ; \
93
+	done
94
+
95
+circle-ci-check: ${build_image}.created
96
+	@${cidocker} make check-local coveralls
91 97
 
92
-circle-ci:
93
-	@${cidocker} make install-deps build-local check-local coveralls
94
-	make integration-tests
98
+circle-ci-build: ${build_image}.created
99
+	@${cidocker} make build-local
95 100
 
96
-start-services:
97
-	service zookeeper start
101
+circle-ci: circle-ci-check circle-ci-build integration-tests
... ...
@@ -552,7 +552,7 @@ func pushReservation(bytePos, bitPos uint64, head *sequence, release bool) *sequ
552 552
 		}
553 553
 		removeCurrentIfEmpty(&newHead, newSequence, current)
554 554
 		mergeSequences(previous)
555
-	} else if precBlocks == current.count-2 { // Last in sequence (B)
555
+	} else if precBlocks == current.count { // Last in sequence (B)
556 556
 		newSequence.next = current.next
557 557
 		current.next = newSequence
558 558
 		mergeSequences(current)
... ...
@@ -1,12 +1,18 @@
1 1
 machine:
2
-    services:
3
-        - docker
2
+  services:
3
+    - docker
4 4
 
5 5
 dependencies:
6
-    override:
7
-        - echo "Nothing to install"
6
+  override:
7
+    - sudo apt-get update; sudo apt-get install -y iptables zookeeperd
8
+    - go get golang.org/x/tools/cmd/vet
9
+    - go get golang.org/x/tools/cmd/goimports
10
+    - go get golang.org/x/tools/cmd/cover
11
+    - go get github.com/tools/godep
12
+    - go get github.com/golang/lint/golint
13
+    - go get github.com/mattn/goveralls
8 14
 
9 15
 test:
10
-    override:
11
-        - make circle-ci
16
+  override:
17
+    - make circle-ci
12 18
 
... ...
@@ -41,6 +41,9 @@ const (
41 41
 	DefaultGatewayV6AuxKey = "DefaultGatewayIPv6"
42 42
 )
43 43
 
44
+type iptableCleanFunc func() error
45
+type iptablesCleanFuncs []iptableCleanFunc
46
+
44 47
 // configuration info for the "bridge" driver.
45 48
 type configuration struct {
46 49
 	EnableIPForwarding  bool
... ...
@@ -92,12 +95,13 @@ type bridgeEndpoint struct {
92 92
 }
93 93
 
94 94
 type bridgeNetwork struct {
95
-	id         string
96
-	bridge     *bridgeInterface // The bridge's L3 interface
97
-	config     *networkConfiguration
98
-	endpoints  map[string]*bridgeEndpoint // key: endpoint id
99
-	portMapper *portmapper.PortMapper
100
-	driver     *driver // The network's driver
95
+	id            string
96
+	bridge        *bridgeInterface // The bridge's L3 interface
97
+	config        *networkConfiguration
98
+	endpoints     map[string]*bridgeEndpoint // key: endpoint id
99
+	portMapper    *portmapper.PortMapper
100
+	driver        *driver // The network's driver
101
+	iptCleanFuncs iptablesCleanFuncs
101 102
 	sync.Mutex
102 103
 }
103 104
 
... ...
@@ -236,6 +240,10 @@ func parseErr(label, value, errString string) error {
236 236
 	return types.BadRequestErrorf("failed to parse %s value: %v (%s)", label, value, errString)
237 237
 }
238 238
 
239
+func (n *bridgeNetwork) registerIptCleanFunc(clean iptableCleanFunc) {
240
+	n.iptCleanFuncs = append(n.iptCleanFuncs, clean)
241
+}
242
+
239 243
 func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, error) {
240 244
 	n.Lock()
241 245
 	defer n.Unlock()
... ...
@@ -604,6 +612,10 @@ func (d *driver) createNetwork(config *networkConfiguration) error {
604 604
 			}
605 605
 			return err
606 606
 		}
607
+		network.registerIptCleanFunc(func() error {
608
+			nwList := d.getNetworks()
609
+			return network.isolateNetwork(nwList, false)
610
+		})
607 611
 		return nil
608 612
 	}
609 613
 
... ...
@@ -722,22 +734,6 @@ func (d *driver) DeleteNetwork(nid string) error {
722 722
 		return err
723 723
 	}
724 724
 
725
-	// In case of failures after this point, restore the network isolation rules
726
-	nwList := d.getNetworks()
727
-	defer func() {
728
-		if err != nil {
729
-			if err := n.isolateNetwork(nwList, true); err != nil {
730
-				logrus.Warnf("Failed on restoring the inter-network iptables rules on cleanup: %v", err)
731
-			}
732
-		}
733
-	}()
734
-
735
-	// Remove inter-network communication rules.
736
-	err = n.isolateNetwork(nwList, false)
737
-	if err != nil {
738
-		return err
739
-	}
740
-
741 725
 	// We only delete the bridge when it's not the default bridge. This is keep the backward compatible behavior.
742 726
 	if !config.DefaultBridge {
743 727
 		if err := netlink.LinkDel(n.bridge.Link); err != nil {
... ...
@@ -745,6 +741,12 @@ func (d *driver) DeleteNetwork(nid string) error {
745 745
 		}
746 746
 	}
747 747
 
748
+	// clean all relevant iptables rules
749
+	for _, cleanFunc := range n.iptCleanFuncs {
750
+		if errClean := cleanFunc(); errClean != nil {
751
+			logrus.Warnf("Failed to clean iptables rules for bridge network: %v", errClean)
752
+		}
753
+	}
748 754
 	return d.storeDelete(config)
749 755
 }
750 756
 
... ...
@@ -68,21 +68,27 @@ func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInt
68 68
 	if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
69 69
 		return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
70 70
 	}
71
+	n.registerIptCleanFunc(func() error {
72
+		return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
73
+	})
71 74
 
72 75
 	natChain, filterChain, err := n.getDriverChains()
73 76
 	if err != nil {
74 77
 		return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
75 78
 	}
76 79
 
77
-	err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode)
80
+	err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
78 81
 	if err != nil {
79 82
 		return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
80 83
 	}
81 84
 
82
-	err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode)
85
+	err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
83 86
 	if err != nil {
84 87
 		return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
85 88
 	}
89
+	n.registerIptCleanFunc(func() error {
90
+		return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
91
+	})
86 92
 
87 93
 	n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName())
88 94
 
... ...
@@ -159,7 +159,11 @@ func (ep *endpoint) Info() EndpointInfo {
159 159
 		return ep
160 160
 	}
161 161
 
162
-	return sb.getEndpoint(ep.ID())
162
+	if epi := sb.getEndpoint(ep.ID()); epi != nil {
163
+		return epi
164
+	}
165
+
166
+	return nil
163 167
 }
164 168
 
165 169
 func (ep *endpoint) DriverInfo() (map[string]interface{}, error) {
... ...
@@ -27,7 +27,7 @@ const (
27 27
 // Conn is a connection to firewalld dbus endpoint.
28 28
 type Conn struct {
29 29
 	sysconn *dbus.Conn
30
-	sysobj  *dbus.Object
30
+	sysobj  dbus.BusObject
31 31
 	signal  chan *dbus.Signal
32 32
 }
33 33
 
... ...
@@ -95,7 +95,7 @@ func NewChain(name string, table Table, hairpinMode bool) (*ChainInfo, error) {
95 95
 }
96 96
 
97 97
 // ProgramChain is used to add rules to a chain
98
-func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode bool) error {
98
+func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) error {
99 99
 	if c.Name == "" {
100 100
 		return fmt.Errorf("Could not program chain, missing chain name.")
101 101
 	}
... ...
@@ -106,10 +106,14 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode bool) error {
106 106
 			"-m", "addrtype",
107 107
 			"--dst-type", "LOCAL",
108 108
 			"-j", c.Name}
109
-		if !Exists(Nat, "PREROUTING", preroute...) {
109
+		if !Exists(Nat, "PREROUTING", preroute...) && enable {
110 110
 			if err := c.Prerouting(Append, preroute...); err != nil {
111 111
 				return fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
112 112
 			}
113
+		} else if Exists(Nat, "PREROUTING", preroute...) && !enable {
114
+			if err := c.Prerouting(Delete, preroute...); err != nil {
115
+				return fmt.Errorf("Failed to remove docker in PREROUTING chain: %s", err)
116
+			}
113 117
 		}
114 118
 		output := []string{
115 119
 			"-m", "addrtype",
... ...
@@ -118,10 +122,14 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode bool) error {
118 118
 		if !hairpinMode {
119 119
 			output = append(output, "!", "--dst", "127.0.0.0/8")
120 120
 		}
121
-		if !Exists(Nat, "OUTPUT", output...) {
121
+		if !Exists(Nat, "OUTPUT", output...) && enable {
122 122
 			if err := c.Output(Append, output...); err != nil {
123 123
 				return fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
124 124
 			}
125
+		} else if Exists(Nat, "OUTPUT", output...) && !enable {
126
+			if err := c.Output(Delete, output...); err != nil {
127
+				return fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
128
+			}
125 129
 		}
126 130
 	case Filter:
127 131
 		if bridgeName == "" {
... ...
@@ -131,13 +139,21 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode bool) error {
131 131
 		link := []string{
132 132
 			"-o", bridgeName,
133 133
 			"-j", c.Name}
134
-		if !Exists(Filter, "FORWARD", link...) {
134
+		if !Exists(Filter, "FORWARD", link...) && enable {
135 135
 			insert := append([]string{string(Insert), "FORWARD"}, link...)
136 136
 			if output, err := Raw(insert...); err != nil {
137 137
 				return err
138 138
 			} else if len(output) != 0 {
139 139
 				return fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output)
140 140
 			}
141
+		} else if Exists(Filter, "FORWARD", link...) && !enable {
142
+			del := append([]string{string(Delete), "FORWARD"}, link...)
143
+			if output, err := Raw(del...); err != nil {
144
+				return err
145
+			} else if len(output) != 0 {
146
+				return fmt.Errorf("Could not delete linking rule from %s/%s: %s", c.Table, c.Name, output)
147
+			}
148
+
141 149
 		}
142 150
 	}
143 151
 	return nil
... ...
@@ -9,10 +9,8 @@ import (
9 9
 	"fmt"
10 10
 	"io"
11 11
 	"net"
12
-	"strings"
13 12
 
14 13
 	"github.com/docker/libnetwork/types"
15
-	"github.com/vishvananda/netlink"
16 14
 )
17 15
 
18 16
 var (
... ...
@@ -22,8 +20,6 @@ var (
22 22
 	ErrNetworkOverlaps = errors.New("requested network overlaps with existing network")
23 23
 	// ErrNoDefaultRoute preformatted error
24 24
 	ErrNoDefaultRoute = errors.New("no default route")
25
-
26
-	networkGetRoutesFct = netlink.RouteList
27 25
 )
28 26
 
29 27
 // CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers
... ...
@@ -42,21 +38,6 @@ func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
42 42
 	return nil
43 43
 }
44 44
 
45
-// CheckRouteOverlaps checks whether the passed network overlaps with any existing routes
46
-func CheckRouteOverlaps(toCheck *net.IPNet) error {
47
-	networks, err := networkGetRoutesFct(nil, netlink.FAMILY_V4)
48
-	if err != nil {
49
-		return err
50
-	}
51
-
52
-	for _, network := range networks {
53
-		if network.Dst != nil && NetworkOverlaps(toCheck, network.Dst) {
54
-			return ErrNetworkOverlaps
55
-		}
56
-	}
57
-	return nil
58
-}
59
-
60 45
 // NetworkOverlaps detects overlap between one IPNet and another
61 46
 func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool {
62 47
 	return netX.Contains(netY.IP) || netY.Contains(netX.IP)
... ...
@@ -151,22 +132,3 @@ func GenerateRandomName(prefix string, size int) (string, error) {
151 151
 	}
152 152
 	return prefix + hex.EncodeToString(id)[:size], nil
153 153
 }
154
-
155
-// GenerateIfaceName returns an interface name using the passed in
156
-// prefix and the length of random bytes. The api ensures that the
157
-// there are is no interface which exists with that name.
158
-func GenerateIfaceName(prefix string, len int) (string, error) {
159
-	for i := 0; i < 3; i++ {
160
-		name, err := GenerateRandomName(prefix, len)
161
-		if err != nil {
162
-			continue
163
-		}
164
-		if _, err := netlink.LinkByName(name); err != nil {
165
-			if strings.Contains(err.Error(), "not found") {
166
-				return name, nil
167
-			}
168
-			return "", err
169
-		}
170
-	}
171
-	return "", types.InternalErrorf("could not generate interface name")
172
-}
173 154
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+// +build linux
1
+// Network utility functions.
2
+
3
+package netutils
4
+
5
+import (
6
+	"net"
7
+	"strings"
8
+
9
+	"github.com/docker/libnetwork/types"
10
+	"github.com/vishvananda/netlink"
11
+)
12
+
13
+var (
14
+	networkGetRoutesFct = netlink.RouteList
15
+)
16
+
17
+// CheckRouteOverlaps checks whether the passed network overlaps with any existing routes
18
+func CheckRouteOverlaps(toCheck *net.IPNet) error {
19
+	networks, err := networkGetRoutesFct(nil, netlink.FAMILY_V4)
20
+	if err != nil {
21
+		return err
22
+	}
23
+
24
+	for _, network := range networks {
25
+		if network.Dst != nil && NetworkOverlaps(toCheck, network.Dst) {
26
+			return ErrNetworkOverlaps
27
+		}
28
+	}
29
+	return nil
30
+}
31
+
32
+// GenerateIfaceName returns an interface name using the passed in
33
+// prefix and the length of random bytes. The api ensures that the
34
+// there are is no interface which exists with that name.
35
+func GenerateIfaceName(prefix string, len int) (string, error) {
36
+	for i := 0; i < 3; i++ {
37
+		name, err := GenerateRandomName(prefix, len)
38
+		if err != nil {
39
+			continue
40
+		}
41
+		if _, err := netlink.LinkByName(name); err != nil {
42
+			if strings.Contains(err.Error(), "not found") {
43
+				return name, nil
44
+			}
45
+			return "", err
46
+		}
47
+	}
48
+	return "", types.InternalErrorf("could not generate interface name")
49
+}
... ...
@@ -671,6 +671,12 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
671 671
 
672 672
 	ep.processOptions(options...)
673 673
 
674
+	if opt, ok := ep.generic[netlabel.MacAddress]; ok {
675
+		if mac, ok := opt.(net.HardwareAddr); ok {
676
+			ep.iface.mac = mac
677
+		}
678
+	}
679
+
674 680
 	if err = ep.assignAddress(true, !n.postIPv6); err != nil {
675 681
 		return nil, err
676 682
 	}
... ...
@@ -1,19 +1,6 @@
1 1
 package libnetwork
2 2
 
3
-import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"io"
7
-	"io/ioutil"
8
-	"net"
9
-	"os"
10
-
11
-	"github.com/Sirupsen/logrus"
12
-	"github.com/docker/docker/pkg/reexec"
13
-	"github.com/docker/libnetwork/types"
14
-	"github.com/opencontainers/runc/libcontainer"
15
-	"github.com/opencontainers/runc/libcontainer/configs"
16
-)
3
+import "github.com/docker/docker/pkg/reexec"
17 4
 
18 5
 type setKeyData struct {
19 6
 	ContainerID string
... ...
@@ -23,163 +10,3 @@ type setKeyData struct {
23 23
 func init() {
24 24
 	reexec.Register("libnetwork-setkey", processSetKeyReexec)
25 25
 }
26
-
27
-const udsBase = "/var/lib/docker/network/files/"
28
-const success = "success"
29
-
30
-// processSetKeyReexec is a private function that must be called only on an reexec path
31
-// It expects 3 args { [0] = "libnetwork-setkey", [1] = <container-id>, [2] = <controller-id> }
32
-// It also expects libcontainer.State as a json string in <stdin>
33
-// Refer to https://github.com/opencontainers/runc/pull/160/ for more information
34
-func processSetKeyReexec() {
35
-	var err error
36
-
37
-	// Return a failure to the calling process via ExitCode
38
-	defer func() {
39
-		if err != nil {
40
-			logrus.Fatalf("%v", err)
41
-		}
42
-	}()
43
-
44
-	// expecting 3 args {[0]="libnetwork-setkey", [1]=<container-id>, [2]=<controller-id> }
45
-	if len(os.Args) < 3 {
46
-		err = fmt.Errorf("Re-exec expects 3 args, received : %d", len(os.Args))
47
-		return
48
-	}
49
-	containerID := os.Args[1]
50
-
51
-	// We expect libcontainer.State as a json string in <stdin>
52
-	stateBuf, err := ioutil.ReadAll(os.Stdin)
53
-	if err != nil {
54
-		return
55
-	}
56
-	var state libcontainer.State
57
-	if err = json.Unmarshal(stateBuf, &state); err != nil {
58
-		return
59
-	}
60
-
61
-	controllerID := os.Args[2]
62
-	key := state.NamespacePaths[configs.NamespaceType("NEWNET")]
63
-
64
-	err = SetExternalKey(controllerID, containerID, key)
65
-	return
66
-}
67
-
68
-// SetExternalKey provides a convenient way to set an External key to a sandbox
69
-func SetExternalKey(controllerID string, containerID string, key string) error {
70
-	keyData := setKeyData{
71
-		ContainerID: containerID,
72
-		Key:         key}
73
-
74
-	c, err := net.Dial("unix", udsBase+controllerID+".sock")
75
-	if err != nil {
76
-		return err
77
-	}
78
-	defer c.Close()
79
-
80
-	if err = sendKey(c, keyData); err != nil {
81
-		return fmt.Errorf("sendKey failed with : %v", err)
82
-	}
83
-	return processReturn(c)
84
-}
85
-
86
-func sendKey(c net.Conn, data setKeyData) error {
87
-	var err error
88
-	defer func() {
89
-		if err != nil {
90
-			c.Close()
91
-		}
92
-	}()
93
-
94
-	var b []byte
95
-	if b, err = json.Marshal(data); err != nil {
96
-		return err
97
-	}
98
-
99
-	_, err = c.Write(b)
100
-	return err
101
-}
102
-
103
-func processReturn(r io.Reader) error {
104
-	buf := make([]byte, 1024)
105
-	n, err := r.Read(buf[:])
106
-	if err != nil {
107
-		return fmt.Errorf("failed to read buf in processReturn : %v", err)
108
-	}
109
-	if string(buf[0:n]) != success {
110
-		return fmt.Errorf(string(buf[0:n]))
111
-	}
112
-	return nil
113
-}
114
-
115
-func (c *controller) startExternalKeyListener() error {
116
-	if err := os.MkdirAll(udsBase, 0600); err != nil {
117
-		return err
118
-	}
119
-	uds := udsBase + c.id + ".sock"
120
-	l, err := net.Listen("unix", uds)
121
-	if err != nil {
122
-		return err
123
-	}
124
-	if err := os.Chmod(uds, 0600); err != nil {
125
-		l.Close()
126
-		return err
127
-	}
128
-	c.Lock()
129
-	c.extKeyListener = l
130
-	c.Unlock()
131
-
132
-	go c.acceptClientConnections(uds, l)
133
-	return nil
134
-}
135
-
136
-func (c *controller) acceptClientConnections(sock string, l net.Listener) {
137
-	for {
138
-		conn, err := l.Accept()
139
-		if err != nil {
140
-			if _, err1 := os.Stat(sock); os.IsNotExist(err1) {
141
-				logrus.Debugf("Unix socket %s doesnt exist. cannot accept client connections", sock)
142
-				return
143
-			}
144
-			logrus.Errorf("Error accepting connection %v", err)
145
-			continue
146
-		}
147
-		go func() {
148
-			err := c.processExternalKey(conn)
149
-			ret := success
150
-			if err != nil {
151
-				ret = err.Error()
152
-			}
153
-
154
-			_, err = conn.Write([]byte(ret))
155
-			if err != nil {
156
-				logrus.Errorf("Error returning to the client %v", err)
157
-			}
158
-		}()
159
-	}
160
-}
161
-
162
-func (c *controller) processExternalKey(conn net.Conn) error {
163
-	buf := make([]byte, 1280)
164
-	nr, err := conn.Read(buf)
165
-	if err != nil {
166
-		return err
167
-	}
168
-	var s setKeyData
169
-	if err = json.Unmarshal(buf[0:nr], &s); err != nil {
170
-		return err
171
-	}
172
-
173
-	var sandbox Sandbox
174
-	search := SandboxContainerWalker(&sandbox, s.ContainerID)
175
-	c.WalkSandboxes(search)
176
-	if sandbox == nil {
177
-		return types.BadRequestErrorf("no sandbox present for %s", s.ContainerID)
178
-	}
179
-
180
-	return sandbox.SetKey(s.Key)
181
-}
182
-
183
-func (c *controller) stopExternalKeyListener() {
184
-	c.extKeyListener.Close()
185
-}
186 26
new file mode 100644
... ...
@@ -0,0 +1,177 @@
0
+// +build !windows
1
+
2
+package libnetwork
3
+
4
+import (
5
+	"encoding/json"
6
+	"fmt"
7
+	"io"
8
+	"io/ioutil"
9
+	"net"
10
+	"os"
11
+
12
+	"github.com/Sirupsen/logrus"
13
+	"github.com/docker/libnetwork/types"
14
+	"github.com/opencontainers/runc/libcontainer"
15
+	"github.com/opencontainers/runc/libcontainer/configs"
16
+)
17
+
18
+const udsBase = "/var/lib/docker/network/files/"
19
+const success = "success"
20
+
21
+// processSetKeyReexec is a private function that must be called only on an reexec path
22
+// It expects 3 args { [0] = "libnetwork-setkey", [1] = <container-id>, [2] = <controller-id> }
23
+// It also expects libcontainer.State as a json string in <stdin>
24
+// Refer to https://github.com/opencontainers/runc/pull/160/ for more information
25
+func processSetKeyReexec() {
26
+	var err error
27
+
28
+	// Return a failure to the calling process via ExitCode
29
+	defer func() {
30
+		if err != nil {
31
+			logrus.Fatalf("%v", err)
32
+		}
33
+	}()
34
+
35
+	// expecting 3 args {[0]="libnetwork-setkey", [1]=<container-id>, [2]=<controller-id> }
36
+	if len(os.Args) < 3 {
37
+		err = fmt.Errorf("Re-exec expects 3 args, received : %d", len(os.Args))
38
+		return
39
+	}
40
+	containerID := os.Args[1]
41
+
42
+	// We expect libcontainer.State as a json string in <stdin>
43
+	stateBuf, err := ioutil.ReadAll(os.Stdin)
44
+	if err != nil {
45
+		return
46
+	}
47
+	var state libcontainer.State
48
+	if err = json.Unmarshal(stateBuf, &state); err != nil {
49
+		return
50
+	}
51
+
52
+	controllerID := os.Args[2]
53
+	key := state.NamespacePaths[configs.NamespaceType("NEWNET")]
54
+
55
+	err = SetExternalKey(controllerID, containerID, key)
56
+	return
57
+}
58
+
59
+// SetExternalKey provides a convenient way to set an External key to a sandbox
60
+func SetExternalKey(controllerID string, containerID string, key string) error {
61
+	keyData := setKeyData{
62
+		ContainerID: containerID,
63
+		Key:         key}
64
+
65
+	c, err := net.Dial("unix", udsBase+controllerID+".sock")
66
+	if err != nil {
67
+		return err
68
+	}
69
+	defer c.Close()
70
+
71
+	if err = sendKey(c, keyData); err != nil {
72
+		return fmt.Errorf("sendKey failed with : %v", err)
73
+	}
74
+	return processReturn(c)
75
+}
76
+
77
+func sendKey(c net.Conn, data setKeyData) error {
78
+	var err error
79
+	defer func() {
80
+		if err != nil {
81
+			c.Close()
82
+		}
83
+	}()
84
+
85
+	var b []byte
86
+	if b, err = json.Marshal(data); err != nil {
87
+		return err
88
+	}
89
+
90
+	_, err = c.Write(b)
91
+	return err
92
+}
93
+
94
+func processReturn(r io.Reader) error {
95
+	buf := make([]byte, 1024)
96
+	n, err := r.Read(buf[:])
97
+	if err != nil {
98
+		return fmt.Errorf("failed to read buf in processReturn : %v", err)
99
+	}
100
+	if string(buf[0:n]) != success {
101
+		return fmt.Errorf(string(buf[0:n]))
102
+	}
103
+	return nil
104
+}
105
+
106
+func (c *controller) startExternalKeyListener() error {
107
+	if err := os.MkdirAll(udsBase, 0600); err != nil {
108
+		return err
109
+	}
110
+	uds := udsBase + c.id + ".sock"
111
+	l, err := net.Listen("unix", uds)
112
+	if err != nil {
113
+		return err
114
+	}
115
+	if err := os.Chmod(uds, 0600); err != nil {
116
+		l.Close()
117
+		return err
118
+	}
119
+	c.Lock()
120
+	c.extKeyListener = l
121
+	c.Unlock()
122
+
123
+	go c.acceptClientConnections(uds, l)
124
+	return nil
125
+}
126
+
127
+func (c *controller) acceptClientConnections(sock string, l net.Listener) {
128
+	for {
129
+		conn, err := l.Accept()
130
+		if err != nil {
131
+			if _, err1 := os.Stat(sock); os.IsNotExist(err1) {
132
+				logrus.Debugf("Unix socket %s doesnt exist. cannot accept client connections", sock)
133
+				return
134
+			}
135
+			logrus.Errorf("Error accepting connection %v", err)
136
+			continue
137
+		}
138
+		go func() {
139
+			err := c.processExternalKey(conn)
140
+			ret := success
141
+			if err != nil {
142
+				ret = err.Error()
143
+			}
144
+
145
+			_, err = conn.Write([]byte(ret))
146
+			if err != nil {
147
+				logrus.Errorf("Error returning to the client %v", err)
148
+			}
149
+		}()
150
+	}
151
+}
152
+
153
+func (c *controller) processExternalKey(conn net.Conn) error {
154
+	buf := make([]byte, 1280)
155
+	nr, err := conn.Read(buf)
156
+	if err != nil {
157
+		return err
158
+	}
159
+	var s setKeyData
160
+	if err = json.Unmarshal(buf[0:nr], &s); err != nil {
161
+		return err
162
+	}
163
+
164
+	var sandbox Sandbox
165
+	search := SandboxContainerWalker(&sandbox, s.ContainerID)
166
+	c.WalkSandboxes(search)
167
+	if sandbox == nil {
168
+		return types.BadRequestErrorf("no sandbox present for %s", s.ContainerID)
169
+	}
170
+
171
+	return sandbox.SetKey(s.Key)
172
+}
173
+
174
+func (c *controller) stopExternalKeyListener() {
175
+	c.extKeyListener.Close()
176
+}
0 177
new file mode 100644
... ...
@@ -0,0 +1,45 @@
0
+// +build windows
1
+
2
+package libnetwork
3
+
4
+import (
5
+	"io"
6
+	"net"
7
+
8
+	"github.com/docker/libnetwork/types"
9
+)
10
+
11
+// processSetKeyReexec is a private function that must be called only on an reexec path
12
+// It expects 3 args { [0] = "libnetwork-setkey", [1] = <container-id>, [2] = <controller-id> }
13
+// It also expects libcontainer.State as a json string in <stdin>
14
+// Refer to https://github.com/opencontainers/runc/pull/160/ for more information
15
+func processSetKeyReexec() {
16
+}
17
+
18
+// SetExternalKey provides a convenient way to set an External key to a sandbox
19
+func SetExternalKey(controllerID string, containerID string, key string) error {
20
+	return types.NotImplementedErrorf("SetExternalKey isn't supported on non linux systems")
21
+}
22
+
23
+func sendKey(c net.Conn, data setKeyData) error {
24
+	return types.NotImplementedErrorf("sendKey isn't supported on non linux systems")
25
+}
26
+
27
+func processReturn(r io.Reader) error {
28
+	return types.NotImplementedErrorf("processReturn isn't supported on non linux systems")
29
+}
30
+
31
+// no-op on non linux systems
32
+func (c *controller) startExternalKeyListener() error {
33
+	return nil
34
+}
35
+
36
+func (c *controller) acceptClientConnections(sock string, l net.Listener) {
37
+}
38
+
39
+func (c *controller) processExternalKey(conn net.Conn) error {
40
+	return types.NotImplementedErrorf("processExternalKey isn't supported on non linux systems")
41
+}
42
+
43
+func (c *controller) stopExternalKeyListener() {
44
+}
... ...
@@ -1,4 +1,4 @@
1
-Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>)
1
+Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>), Google
2 2
 All rights reserved.
3 3
 
4 4
 Redistribution and use in source and binary forms, with or without
... ...
@@ -27,6 +27,9 @@ The complete package documentation and some simple examples are available at
27 27
 [_examples](https://github.com/godbus/dbus/tree/master/_examples) directory
28 28
 gives a short overview over the basic usage. 
29 29
 
30
+#### Projects using godbus
31
+- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
32
+
30 33
 Please note that the API is considered unstable for now and may change without
31 34
 further notice.
32 35
 
... ...
@@ -2,7 +2,6 @@ package dbus
2 2
 
3 3
 import (
4 4
 	"errors"
5
-	"strings"
6 5
 )
7 6
 
8 7
 // Call represents a pending or completed method call.
... ...
@@ -35,113 +34,3 @@ func (c *Call) Store(retvalues ...interface{}) error {
35 35
 
36 36
 	return Store(c.Body, retvalues...)
37 37
 }
38
-
39
-// Object represents a remote object on which methods can be invoked.
40
-type Object struct {
41
-	conn *Conn
42
-	dest string
43
-	path ObjectPath
44
-}
45
-
46
-// Call calls a method with (*Object).Go and waits for its reply.
47
-func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
48
-	return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
49
-}
50
-
51
-// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
52
-// object. The property name must be given in interface.member notation.
53
-func (o *Object) GetProperty(p string) (Variant, error) {
54
-	idx := strings.LastIndex(p, ".")
55
-	if idx == -1 || idx+1 == len(p) {
56
-		return Variant{}, errors.New("dbus: invalid property " + p)
57
-	}
58
-
59
-	iface := p[:idx]
60
-	prop := p[idx+1:]
61
-
62
-	result := Variant{}
63
-	err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
64
-
65
-	if err != nil {
66
-		return Variant{}, err
67
-	}
68
-
69
-	return result, nil
70
-}
71
-
72
-// Go calls a method with the given arguments asynchronously. It returns a
73
-// Call structure representing this method call. The passed channel will
74
-// return the same value once the call is done. If ch is nil, a new channel
75
-// will be allocated. Otherwise, ch has to be buffered or Go will panic.
76
-//
77
-// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
78
-// is returned of which only the Err member is valid.
79
-//
80
-// If the method parameter contains a dot ('.'), the part before the last dot
81
-// specifies the interface on which the method is called.
82
-func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
83
-	iface := ""
84
-	i := strings.LastIndex(method, ".")
85
-	if i != -1 {
86
-		iface = method[:i]
87
-	}
88
-	method = method[i+1:]
89
-	msg := new(Message)
90
-	msg.Type = TypeMethodCall
91
-	msg.serial = o.conn.getSerial()
92
-	msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
93
-	msg.Headers = make(map[HeaderField]Variant)
94
-	msg.Headers[FieldPath] = MakeVariant(o.path)
95
-	msg.Headers[FieldDestination] = MakeVariant(o.dest)
96
-	msg.Headers[FieldMember] = MakeVariant(method)
97
-	if iface != "" {
98
-		msg.Headers[FieldInterface] = MakeVariant(iface)
99
-	}
100
-	msg.Body = args
101
-	if len(args) > 0 {
102
-		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
103
-	}
104
-	if msg.Flags&FlagNoReplyExpected == 0 {
105
-		if ch == nil {
106
-			ch = make(chan *Call, 10)
107
-		} else if cap(ch) == 0 {
108
-			panic("dbus: unbuffered channel passed to (*Object).Go")
109
-		}
110
-		call := &Call{
111
-			Destination: o.dest,
112
-			Path:        o.path,
113
-			Method:      method,
114
-			Args:        args,
115
-			Done:        ch,
116
-		}
117
-		o.conn.callsLck.Lock()
118
-		o.conn.calls[msg.serial] = call
119
-		o.conn.callsLck.Unlock()
120
-		o.conn.outLck.RLock()
121
-		if o.conn.closed {
122
-			call.Err = ErrClosed
123
-			call.Done <- call
124
-		} else {
125
-			o.conn.out <- msg
126
-		}
127
-		o.conn.outLck.RUnlock()
128
-		return call
129
-	}
130
-	o.conn.outLck.RLock()
131
-	defer o.conn.outLck.RUnlock()
132
-	if o.conn.closed {
133
-		return &Call{Err: ErrClosed}
134
-	}
135
-	o.conn.out <- msg
136
-	return &Call{Err: nil}
137
-}
138
-
139
-// Destination returns the destination that calls on o are sent to.
140
-func (o *Object) Destination() string {
141
-	return o.dest
142
-}
143
-
144
-// Path returns the path that calls on o are sent to.
145
-func (o *Object) Path() ObjectPath {
146
-	return o.path
147
-}
... ...
@@ -32,7 +32,7 @@ var ErrClosed = errors.New("dbus: connection closed by user")
32 32
 type Conn struct {
33 33
 	transport
34 34
 
35
-	busObj *Object
35
+	busObj BusObject
36 36
 	unixFD bool
37 37
 	uuid   string
38 38
 
... ...
@@ -46,7 +46,7 @@ type Conn struct {
46 46
 	calls    map[uint32]*Call
47 47
 	callsLck sync.RWMutex
48 48
 
49
-	handlers    map[ObjectPath]map[string]interface{}
49
+	handlers    map[ObjectPath]map[string]exportWithMapping
50 50
 	handlersLck sync.RWMutex
51 51
 
52 52
 	out    chan *Message
... ...
@@ -157,7 +157,7 @@ func newConn(tr transport) (*Conn, error) {
157 157
 	conn.transport = tr
158 158
 	conn.calls = make(map[uint32]*Call)
159 159
 	conn.out = make(chan *Message, 10)
160
-	conn.handlers = make(map[ObjectPath]map[string]interface{})
160
+	conn.handlers = make(map[ObjectPath]map[string]exportWithMapping)
161 161
 	conn.nextSerial = 1
162 162
 	conn.serialUsed = map[uint32]bool{0: true}
163 163
 	conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
... ...
@@ -166,7 +166,7 @@ func newConn(tr transport) (*Conn, error) {
166 166
 
167 167
 // BusObject returns the object owned by the bus daemon which handles
168 168
 // administrative requests.
169
-func (conn *Conn) BusObject() *Object {
169
+func (conn *Conn) BusObject() BusObject {
170 170
 	return conn.busObj
171 171
 }
172 172
 
... ...
@@ -303,18 +303,33 @@ func (conn *Conn) inWorker() {
303 303
 				// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
304 304
 				// sender is optional for signals.
305 305
 				sender, _ := msg.Headers[FieldSender].value.(string)
306
-				if iface == "org.freedesktop.DBus" && member == "NameLost" &&
307
-					sender == "org.freedesktop.DBus" {
308
-
309
-					name, _ := msg.Body[0].(string)
310
-					conn.namesLck.Lock()
311
-					for i, v := range conn.names {
312
-						if v == name {
313
-							copy(conn.names[i:], conn.names[i+1:])
314
-							conn.names = conn.names[:len(conn.names)-1]
306
+				if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" {
307
+					if member == "NameLost" {
308
+						// If we lost the name on the bus, remove it from our
309
+						// tracking list.
310
+						name, ok := msg.Body[0].(string)
311
+						if !ok {
312
+							panic("Unable to read the lost name")
315 313
 						}
314
+						conn.namesLck.Lock()
315
+						for i, v := range conn.names {
316
+							if v == name {
317
+								conn.names = append(conn.names[:i],
318
+									conn.names[i+1:]...)
319
+							}
320
+						}
321
+						conn.namesLck.Unlock()
322
+					} else if member == "NameAcquired" {
323
+						// If we acquired the name on the bus, add it to our
324
+						// tracking list.
325
+						name, ok := msg.Body[0].(string)
326
+						if !ok {
327
+							panic("Unable to read the acquired name")
328
+						}
329
+						conn.namesLck.Lock()
330
+						conn.names = append(conn.names, name)
331
+						conn.namesLck.Unlock()
316 332
 					}
317
-					conn.namesLck.Unlock()
318 333
 				}
319 334
 				signal := &Signal{
320 335
 					Sender: sender,
... ...
@@ -360,7 +375,7 @@ func (conn *Conn) Names() []string {
360 360
 }
361 361
 
362 362
 // Object returns the object identified by the given destination name and path.
363
-func (conn *Conn) Object(dest string, path ObjectPath) *Object {
363
+func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
364 364
 	return &Object{conn, dest, path}
365 365
 }
366 366
 
... ...
@@ -554,7 +569,7 @@ type transport interface {
554 554
 }
555 555
 
556 556
 var (
557
-	transports map[string]func(string) (transport, error) = make(map[string]func(string) (transport, error))
557
+	transports = make(map[string]func(string) (transport, error))
558 558
 )
559 559
 
560 560
 func getTransport(address string) (transport, error) {
... ...
@@ -571,6 +586,7 @@ func getTransport(address string) (transport, error) {
571 571
 		f := transports[v[:i]]
572 572
 		if f == nil {
573 573
 			err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
574
+			continue
574 575
 		}
575 576
 		t, err = f(v[i+1:])
576 577
 		if err == nil {
... ...
@@ -16,24 +16,43 @@ type encoder struct {
16 16
 
17 17
 // NewEncoder returns a new encoder that writes to out in the given byte order.
18 18
 func newEncoder(out io.Writer, order binary.ByteOrder) *encoder {
19
+	return newEncoderAtOffset(out, 0, order)
20
+}
21
+
22
+// newEncoderAtOffset returns a new encoder that writes to out in the given
23
+// byte order. Specify the offset to initialize pos for proper alignment
24
+// computation.
25
+func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder) *encoder {
19 26
 	enc := new(encoder)
20 27
 	enc.out = out
21 28
 	enc.order = order
29
+	enc.pos = offset
22 30
 	return enc
23 31
 }
24 32
 
25 33
 // Aligns the next output to be on a multiple of n. Panics on write errors.
26 34
 func (enc *encoder) align(n int) {
27
-	if enc.pos%n != 0 {
28
-		newpos := (enc.pos + n - 1) & ^(n - 1)
29
-		empty := make([]byte, newpos-enc.pos)
35
+	pad := enc.padding(0, n)
36
+	if pad > 0 {
37
+		empty := make([]byte, pad)
30 38
 		if _, err := enc.out.Write(empty); err != nil {
31 39
 			panic(err)
32 40
 		}
33
-		enc.pos = newpos
41
+		enc.pos += pad
34 42
 	}
35 43
 }
36 44
 
45
+// pad returns the number of bytes of padding, based on current position and additional offset.
46
+// and alignment.
47
+func (enc *encoder) padding(offset, algn int) int {
48
+	abs := enc.pos + offset
49
+	if abs%algn != 0 {
50
+		newabs := (abs + algn - 1) & ^(algn - 1)
51
+		return newabs - abs
52
+	}
53
+	return 0
54
+}
55
+
37 56
 // Calls binary.Write(enc.out, enc.order, v) and panics on write errors.
38 57
 func (enc *encoder) binwrite(v interface{}) {
39 58
 	if err := binary.Write(enc.out, enc.order, v); err != nil {
... ...
@@ -108,8 +127,13 @@ func (enc *encoder) encode(v reflect.Value, depth int) {
108 108
 		if depth >= 64 {
109 109
 			panic(FormatError("input exceeds container depth limit"))
110 110
 		}
111
+		// Lookahead offset: 4 bytes for uint32 length (with alignment),
112
+		// plus alignment for elements.
113
+		n := enc.padding(0, 4) + 4
114
+		offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem()))
115
+
111 116
 		var buf bytes.Buffer
112
-		bufenc := newEncoder(&buf, enc.order)
117
+		bufenc := newEncoderAtOffset(&buf, offset, enc.order)
113 118
 
114 119
 		for i := 0; i < v.Len(); i++ {
115 120
 			bufenc.encode(v.Index(i), depth+1)
... ...
@@ -159,8 +183,13 @@ func (enc *encoder) encode(v reflect.Value, depth int) {
159 159
 			panic(InvalidTypeError{v.Type()})
160 160
 		}
161 161
 		keys := v.MapKeys()
162
+		// Lookahead offset: 4 bytes for uint32 length (with alignment),
163
+		// plus 8-byte alignment
164
+		n := enc.padding(0, 4) + 4
165
+		offset := enc.pos + n + enc.padding(n, 8)
166
+
162 167
 		var buf bytes.Buffer
163
-		bufenc := newEncoder(&buf, enc.order)
168
+		bufenc := newEncoderAtOffset(&buf, offset, enc.order)
164 169
 		for _, k := range keys {
165 170
 			bufenc.align(8)
166 171
 			bufenc.encode(k, depth+2)
... ...
@@ -2,9 +2,9 @@ package dbus
2 2
 
3 3
 import (
4 4
 	"errors"
5
+	"fmt"
5 6
 	"reflect"
6 7
 	"strings"
7
-	"unicode"
8 8
 )
9 9
 
10 10
 var (
... ...
@@ -22,16 +22,52 @@ var (
22 22
 	}
23 23
 )
24 24
 
25
+// exportWithMapping represents an exported struct along with a method name
26
+// mapping to allow for exporting lower-case methods, etc.
27
+type exportWithMapping struct {
28
+	export interface{}
29
+
30
+	// Method name mapping; key -> struct method, value -> dbus method.
31
+	mapping map[string]string
32
+
33
+	// Whether or not this export is for the entire subtree
34
+	includeSubtree bool
35
+}
36
+
25 37
 // Sender is a type which can be used in exported methods to receive the message
26 38
 // sender.
27 39
 type Sender string
28 40
 
29
-func exportedMethod(v interface{}, name string) reflect.Value {
30
-	if v == nil {
41
+func exportedMethod(export exportWithMapping, name string) reflect.Value {
42
+	if export.export == nil {
31 43
 		return reflect.Value{}
32 44
 	}
33
-	m := reflect.ValueOf(v).MethodByName(name)
34
-	if !m.IsValid() {
45
+
46
+	// If a mapping was included in the export, check the map to see if we
47
+	// should be looking for a different method in the export.
48
+	if export.mapping != nil {
49
+		for key, value := range export.mapping {
50
+			if value == name {
51
+				name = key
52
+				break
53
+			}
54
+
55
+			// Catch the case where a method is aliased but the client is calling
56
+			// the original, e.g. the "Foo" method was exported mapped to
57
+			// "foo," and dbus client called the original "Foo."
58
+			if key == name {
59
+				return reflect.Value{}
60
+			}
61
+		}
62
+	}
63
+
64
+	value := reflect.ValueOf(export.export)
65
+	m := value.MethodByName(name)
66
+
67
+	// Catch the case of attempting to call an unexported method
68
+	method, ok := value.Type().MethodByName(name)
69
+
70
+	if !m.IsValid() || !ok || method.PkgPath != "" {
35 71
 		return reflect.Value{}
36 72
 	}
37 73
 	t := m.Type()
... ...
@@ -43,6 +79,42 @@ func exportedMethod(v interface{}, name string) reflect.Value {
43 43
 	return m
44 44
 }
45 45
 
46
+// searchHandlers will look through all registered handlers looking for one
47
+// to handle the given path. If a verbatim one isn't found, it will check for
48
+// a subtree registration for the path as well.
49
+func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, bool) {
50
+	conn.handlersLck.RLock()
51
+	defer conn.handlersLck.RUnlock()
52
+
53
+	handlers, ok := conn.handlers[path]
54
+	if ok {
55
+		return handlers, ok
56
+	}
57
+
58
+	// If handlers weren't found for this exact path, look for a matching subtree
59
+	// registration
60
+	handlers = make(map[string]exportWithMapping)
61
+	path = path[:strings.LastIndex(string(path), "/")]
62
+	for len(path) > 0 {
63
+		var subtreeHandlers map[string]exportWithMapping
64
+		subtreeHandlers, ok = conn.handlers[path]
65
+		if ok {
66
+			for iface, handler := range subtreeHandlers {
67
+				// Only include this handler if it registered for the subtree
68
+				if handler.includeSubtree {
69
+					handlers[iface] = handler
70
+				}
71
+			}
72
+
73
+			break
74
+		}
75
+
76
+		path = path[:strings.LastIndex(string(path), "/")]
77
+	}
78
+
79
+	return handlers, ok
80
+}
81
+
46 82
 // handleCall handles the given method call (i.e. looks if it's one of the
47 83
 // pre-implemented ones and searches for a corresponding handler if not).
48 84
 func (conn *Conn) handleCall(msg *Message) {
... ...
@@ -62,40 +134,35 @@ func (conn *Conn) handleCall(msg *Message) {
62 62
 		}
63 63
 		return
64 64
 	}
65
-	if len(name) == 0 || unicode.IsLower([]rune(name)[0]) {
65
+	if len(name) == 0 {
66 66
 		conn.sendError(errmsgUnknownMethod, sender, serial)
67 67
 	}
68
+
69
+	// Find the exported handler (if any) for this path
70
+	handlers, ok := conn.searchHandlers(path)
71
+	if !ok {
72
+		conn.sendError(errmsgNoObject, sender, serial)
73
+		return
74
+	}
75
+
68 76
 	var m reflect.Value
69 77
 	if hasIface {
70
-		conn.handlersLck.RLock()
71
-		obj, ok := conn.handlers[path]
72
-		if !ok {
73
-			conn.sendError(errmsgNoObject, sender, serial)
74
-			conn.handlersLck.RUnlock()
75
-			return
76
-		}
77
-		iface := obj[ifaceName]
78
-		conn.handlersLck.RUnlock()
78
+		iface := handlers[ifaceName]
79 79
 		m = exportedMethod(iface, name)
80 80
 	} else {
81
-		conn.handlersLck.RLock()
82
-		if _, ok := conn.handlers[path]; !ok {
83
-			conn.sendError(errmsgNoObject, sender, serial)
84
-			conn.handlersLck.RUnlock()
85
-			return
86
-		}
87
-		for _, v := range conn.handlers[path] {
81
+		for _, v := range handlers {
88 82
 			m = exportedMethod(v, name)
89 83
 			if m.IsValid() {
90 84
 				break
91 85
 			}
92 86
 		}
93
-		conn.handlersLck.RUnlock()
94 87
 	}
88
+
95 89
 	if !m.IsValid() {
96 90
 		conn.sendError(errmsgUnknownMethod, sender, serial)
97 91
 		return
98 92
 	}
93
+
99 94
 	t := m.Type()
100 95
 	vs := msg.Body
101 96
 	pointers := make([]interface{}, t.NumIn())
... ...
@@ -106,27 +173,36 @@ func (conn *Conn) handleCall(msg *Message) {
106 106
 		pointers[i] = val.Interface()
107 107
 		if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
108 108
 			val.Elem().SetString(sender)
109
+		} else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
110
+			val.Elem().Set(reflect.ValueOf(*msg))
109 111
 		} else {
110 112
 			decode = append(decode, pointers[i])
111 113
 		}
112 114
 	}
115
+
113 116
 	if len(decode) != len(vs) {
114 117
 		conn.sendError(errmsgInvalidArg, sender, serial)
115 118
 		return
116 119
 	}
120
+
117 121
 	if err := Store(vs, decode...); err != nil {
118 122
 		conn.sendError(errmsgInvalidArg, sender, serial)
119 123
 		return
120 124
 	}
125
+
126
+	// Extract parameters
121 127
 	params := make([]reflect.Value, len(pointers))
122 128
 	for i := 0; i < len(pointers); i++ {
123 129
 		params[i] = reflect.ValueOf(pointers[i]).Elem()
124 130
 	}
131
+
132
+	// Call method
125 133
 	ret := m.Call(params)
126 134
 	if em := ret[t.NumOut()-1].Interface().(*Error); em != nil {
127 135
 		conn.sendError(*em, sender, serial)
128 136
 		return
129 137
 	}
138
+
130 139
 	if msg.Flags&FlagNoReplyExpected == 0 {
131 140
 		reply := new(Message)
132 141
 		reply.Type = TypeMethodReply
... ...
@@ -203,6 +279,10 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro
203 203
 // contribute to the dbus signature of the method (i.e. the method is exposed
204 204
 // as if the parameters of type Sender were not there).
205 205
 //
206
+// Similarly, any parameters with the type Message are set to the raw message
207
+// received on the bus. Again, parameters of this type do not contribute to the
208
+// dbus signature of the method.
209
+//
206 210
 // Every method call is executed in a new goroutine, so the method may be called
207 211
 // in multiple goroutines at once.
208 212
 //
... ...
@@ -214,10 +294,51 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro
214 214
 //
215 215
 // Export returns an error if path is not a valid path name.
216 216
 func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
217
+	return conn.ExportWithMap(v, nil, path, iface)
218
+}
219
+
220
+// ExportWithMap works exactly like Export but provides the ability to remap
221
+// method names (e.g. export a lower-case method).
222
+//
223
+// The keys in the map are the real method names (exported on the struct), and
224
+// the values are the method names to be exported on DBus.
225
+func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
226
+	return conn.exportWithMap(v, mapping, path, iface, false)
227
+}
228
+
229
+// ExportSubtree works exactly like Export but registers the given value for
230
+// an entire subtree rather under the root path provided.
231
+//
232
+// In order to make this useful, one parameter in each of the value's exported
233
+// methods should be a Message, in which case it will contain the raw message
234
+// (allowing one to get access to the path that caused the method to be called).
235
+//
236
+// Note that more specific export paths take precedence over less specific. For
237
+// example, a method call using the ObjectPath /foo/bar/baz will call a method
238
+// exported on /foo/bar before a method exported on /foo.
239
+func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
240
+	return conn.ExportSubtreeWithMap(v, nil, path, iface)
241
+}
242
+
243
+// ExportSubtreeWithMap works exactly like ExportSubtree but provides the
244
+// ability to remap method names (e.g. export a lower-case method).
245
+//
246
+// The keys in the map are the real method names (exported on the struct), and
247
+// the values are the method names to be exported on DBus.
248
+func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
249
+	return conn.exportWithMap(v, mapping, path, iface, true)
250
+}
251
+
252
+// exportWithMap is the worker function for all exports/registrations.
253
+func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string, includeSubtree bool) error {
217 254
 	if !path.IsValid() {
218
-		return errors.New("dbus: invalid path name")
255
+		return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
219 256
 	}
257
+
220 258
 	conn.handlersLck.Lock()
259
+	defer conn.handlersLck.Unlock()
260
+
261
+	// Remove a previous export if the interface is nil
221 262
 	if v == nil {
222 263
 		if _, ok := conn.handlers[path]; ok {
223 264
 			delete(conn.handlers[path], iface)
... ...
@@ -225,51 +346,39 @@ func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
225 225
 				delete(conn.handlers, path)
226 226
 			}
227 227
 		}
228
+
228 229
 		return nil
229 230
 	}
231
+
232
+	// If this is the first handler for this path, make a new map to hold all
233
+	// handlers for this path.
230 234
 	if _, ok := conn.handlers[path]; !ok {
231
-		conn.handlers[path] = make(map[string]interface{})
235
+		conn.handlers[path] = make(map[string]exportWithMapping)
232 236
 	}
233
-	conn.handlers[path][iface] = v
234
-	conn.handlersLck.Unlock()
237
+
238
+	// Finally, save this handler
239
+	conn.handlers[path][iface] = exportWithMapping{export: v, mapping: mapping, includeSubtree: includeSubtree}
240
+
235 241
 	return nil
236 242
 }
237 243
 
238
-// ReleaseName calls org.freedesktop.DBus.ReleaseName. You should use only this
239
-// method to release a name (see below).
244
+// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
240 245
 func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
241 246
 	var r uint32
242 247
 	err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
243 248
 	if err != nil {
244 249
 		return 0, err
245 250
 	}
246
-	if r == uint32(ReleaseNameReplyReleased) {
247
-		conn.namesLck.Lock()
248
-		for i, v := range conn.names {
249
-			if v == name {
250
-				copy(conn.names[i:], conn.names[i+1:])
251
-				conn.names = conn.names[:len(conn.names)-1]
252
-			}
253
-		}
254
-		conn.namesLck.Unlock()
255
-	}
256 251
 	return ReleaseNameReply(r), nil
257 252
 }
258 253
 
259
-// RequestName calls org.freedesktop.DBus.RequestName. You should use only this
260
-// method to request a name because package dbus needs to keep track of all
261
-// names that the connection has.
254
+// RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
262 255
 func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
263 256
 	var r uint32
264 257
 	err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
265 258
 	if err != nil {
266 259
 		return 0, err
267 260
 	}
268
-	if r == uint32(RequestNameReplyPrimaryOwner) {
269
-		conn.namesLck.Lock()
270
-		conn.names = append(conn.names, name)
271
-		conn.namesLck.Unlock()
272
-	}
273 261
 	return RequestNameReply(r), nil
274 262
 }
275 263
 
276 264
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+package dbus
1
+
2
+import (
3
+	"errors"
4
+	"strings"
5
+)
6
+
7
+// BusObject is the interface of a remote object on which methods can be
8
+// invoked.
9
+type BusObject interface {
10
+	Call(method string, flags Flags, args ...interface{}) *Call
11
+	Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call
12
+	GetProperty(p string) (Variant, error)
13
+	Destination() string
14
+	Path() ObjectPath
15
+}
16
+
17
+// Object represents a remote object on which methods can be invoked.
18
+type Object struct {
19
+	conn *Conn
20
+	dest string
21
+	path ObjectPath
22
+}
23
+
24
+// Call calls a method with (*Object).Go and waits for its reply.
25
+func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
26
+	return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
27
+}
28
+
29
+// Go calls a method with the given arguments asynchronously. It returns a
30
+// Call structure representing this method call. The passed channel will
31
+// return the same value once the call is done. If ch is nil, a new channel
32
+// will be allocated. Otherwise, ch has to be buffered or Go will panic.
33
+//
34
+// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
35
+// is returned of which only the Err member is valid.
36
+//
37
+// If the method parameter contains a dot ('.'), the part before the last dot
38
+// specifies the interface on which the method is called.
39
+func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
40
+	iface := ""
41
+	i := strings.LastIndex(method, ".")
42
+	if i != -1 {
43
+		iface = method[:i]
44
+	}
45
+	method = method[i+1:]
46
+	msg := new(Message)
47
+	msg.Type = TypeMethodCall
48
+	msg.serial = o.conn.getSerial()
49
+	msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
50
+	msg.Headers = make(map[HeaderField]Variant)
51
+	msg.Headers[FieldPath] = MakeVariant(o.path)
52
+	msg.Headers[FieldDestination] = MakeVariant(o.dest)
53
+	msg.Headers[FieldMember] = MakeVariant(method)
54
+	if iface != "" {
55
+		msg.Headers[FieldInterface] = MakeVariant(iface)
56
+	}
57
+	msg.Body = args
58
+	if len(args) > 0 {
59
+		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
60
+	}
61
+	if msg.Flags&FlagNoReplyExpected == 0 {
62
+		if ch == nil {
63
+			ch = make(chan *Call, 10)
64
+		} else if cap(ch) == 0 {
65
+			panic("dbus: unbuffered channel passed to (*Object).Go")
66
+		}
67
+		call := &Call{
68
+			Destination: o.dest,
69
+			Path:        o.path,
70
+			Method:      method,
71
+			Args:        args,
72
+			Done:        ch,
73
+		}
74
+		o.conn.callsLck.Lock()
75
+		o.conn.calls[msg.serial] = call
76
+		o.conn.callsLck.Unlock()
77
+		o.conn.outLck.RLock()
78
+		if o.conn.closed {
79
+			call.Err = ErrClosed
80
+			call.Done <- call
81
+		} else {
82
+			o.conn.out <- msg
83
+		}
84
+		o.conn.outLck.RUnlock()
85
+		return call
86
+	}
87
+	o.conn.outLck.RLock()
88
+	defer o.conn.outLck.RUnlock()
89
+	if o.conn.closed {
90
+		return &Call{Err: ErrClosed}
91
+	}
92
+	o.conn.out <- msg
93
+	return &Call{Err: nil}
94
+}
95
+
96
+// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
97
+// object. The property name must be given in interface.member notation.
98
+func (o *Object) GetProperty(p string) (Variant, error) {
99
+	idx := strings.LastIndex(p, ".")
100
+	if idx == -1 || idx+1 == len(p) {
101
+		return Variant{}, errors.New("dbus: invalid property " + p)
102
+	}
103
+
104
+	iface := p[:idx]
105
+	prop := p[idx+1:]
106
+
107
+	result := Variant{}
108
+	err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
109
+
110
+	if err != nil {
111
+		return Variant{}, err
112
+	}
113
+
114
+	return result, nil
115
+}
116
+
117
+// Destination returns the destination that calls on o are sent to.
118
+func (o *Object) Destination() string {
119
+	return o.dest
120
+}
121
+
122
+// Path returns the path that calls on o are sent to.
123
+func (o *Object) Path() ObjectPath {
124
+	return o.path
125
+}
... ...
@@ -39,5 +39,5 @@ test: install generate-test-pbs
39 39
 generate-test-pbs:
40 40
 	make install
41 41
 	make -C testdata
42
-	protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata:. proto3_proto/proto3.proto
42
+	make -C proto3_proto
43 43
 	make
... ...
@@ -1128,12 +1128,10 @@ func size_new_map(p *Properties, base structPointer) int {
1128 1128
 		keycopy.Set(key)
1129 1129
 		valcopy.Set(val)
1130 1130
 
1131
-		// Tag codes for key and val are the responsibility of the sub-sizer.
1132
-		keysize := p.mkeyprop.size(p.mkeyprop, keybase)
1133
-		valsize := p.mvalprop.size(p.mvalprop, valbase)
1134
-		entry := keysize + valsize
1135
-		// Add on tag code and length of map entry itself.
1136
-		n += len(p.tagcode) + sizeVarint(uint64(entry)) + entry
1131
+		// Tag codes are two bytes per map entry.
1132
+		n += 2
1133
+		n += p.mkeyprop.size(p.mkeyprop, keybase)
1134
+		n += p.mvalprop.size(p.mvalprop, valbase)
1137 1135
 	}
1138 1136
 	return n
1139 1137
 }
... ...
@@ -607,15 +607,13 @@ func setDefaults(v reflect.Value, recur, zeros bool) {
607 607
 
608 608
 	for _, ni := range dm.nested {
609 609
 		f := v.Field(ni)
610
-		// f is *T or []*T or map[T]*T
611
-		switch f.Kind() {
612
-		case reflect.Ptr:
613
-			if f.IsNil() {
614
-				continue
615
-			}
610
+		if f.IsNil() {
611
+			continue
612
+		}
613
+		// f is *T or []*T
614
+		if f.Kind() == reflect.Ptr {
616 615
 			setDefaults(f, recur, zeros)
617
-
618
-		case reflect.Slice:
616
+		} else {
619 617
 			for i := 0; i < f.Len(); i++ {
620 618
 				e := f.Index(i)
621 619
 				if e.IsNil() {
... ...
@@ -623,15 +621,6 @@ func setDefaults(v reflect.Value, recur, zeros bool) {
623 623
 				}
624 624
 				setDefaults(e, recur, zeros)
625 625
 			}
626
-
627
-		case reflect.Map:
628
-			for _, k := range f.MapKeys() {
629
-				e := f.MapIndex(k)
630
-				if e.IsNil() {
631
-					continue
632
-				}
633
-				setDefaults(e, recur, zeros)
634
-			}
635 626
 		}
636 627
 	}
637 628
 }
... ...
@@ -657,6 +646,10 @@ type scalarField struct {
657 657
 	value interface{}  // the proto-declared default value, or nil
658 658
 }
659 659
 
660
+func ptrToStruct(t reflect.Type) bool {
661
+	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
662
+}
663
+
660 664
 // t is a struct type.
661 665
 func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
662 666
 	sprop := GetProperties(t)
... ...
@@ -668,33 +661,9 @@ func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
668 668
 		}
669 669
 		ft := t.Field(fi).Type
670 670
 
671
-		var canHaveDefault, nestedMessage bool
672
-		switch ft.Kind() {
673
-		case reflect.Ptr:
674
-			if ft.Elem().Kind() == reflect.Struct {
675
-				nestedMessage = true
676
-			} else {
677
-				canHaveDefault = true // proto2 scalar field
678
-			}
679
-
680
-		case reflect.Slice:
681
-			switch ft.Elem().Kind() {
682
-			case reflect.Ptr:
683
-				nestedMessage = true // repeated message
684
-			case reflect.Uint8:
685
-				canHaveDefault = true // bytes field
686
-			}
687
-
688
-		case reflect.Map:
689
-			if ft.Elem().Kind() == reflect.Ptr {
690
-				nestedMessage = true // map with message values
691
-			}
692
-		}
693
-
694
-		if !canHaveDefault {
695
-			if nestedMessage {
696
-				dm.nested = append(dm.nested, fi)
697
-			}
671
+		// nested messages
672
+		if ptrToStruct(ft) || (ft.Kind() == reflect.Slice && ptrToStruct(ft.Elem())) {
673
+			dm.nested = append(dm.nested, fi)
698 674
 			continue
699 675
 		}
700 676
 
... ...
@@ -440,12 +440,7 @@ func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lock
440 440
 			p.enc = (*Buffer).enc_slice_byte
441 441
 			p.dec = (*Buffer).dec_slice_byte
442 442
 			p.size = size_slice_byte
443
-			// This is a []byte, which is either a bytes field,
444
-			// or the value of a map field. In the latter case,
445
-			// we always encode an empty []byte, so we should not
446
-			// use the proto3 enc/size funcs.
447
-			// f == nil iff this is the key/value of a map field.
448
-			if p.proto3 && f != nil {
443
+			if p.proto3 {
449 444
 				p.enc = (*Buffer).enc_proto3_slice_byte
450 445
 				p.size = size_proto3_slice_byte
451 446
 			}
... ...
@@ -97,12 +97,12 @@ that is local to the container's rootfs.
97 97
 After the container has `/proc` mounted a few standard symlinks are setup 
98 98
 within `/dev/` for the io.
99 99
 
100
-|    Source    | Destination |
101
-| ------------ | ----------- |
102
-| /proc/1/fd   | /dev/fd     |
103
-| /proc/1/fd/0 | /dev/stdin  |
104
-| /proc/1/fd/1 | /dev/stdout |
105
-| /proc/1/fd/2 | /dev/stderr |
100
+|    Source       | Destination |
101
+| --------------- | ----------- |
102
+| /proc/self/fd   | /dev/fd     |
103
+| /proc/self/fd/0 | /dev/stdin  |
104
+| /proc/self/fd/1 | /dev/stdout |
105
+| /proc/self/fd/2 | /dev/stderr |
106 106
 
107 107
 A `pivot_root` is used to change the root for the process, effectively 
108 108
 jailing the process inside the rootfs.
... ...
@@ -3,6 +3,7 @@
3 3
 package fs
4 4
 
5 5
 import (
6
+	"errors"
6 7
 	"fmt"
7 8
 	"io"
8 9
 	"io/ioutil"
... ...
@@ -16,30 +17,45 @@ import (
16 16
 )
17 17
 
18 18
 var (
19
-	subsystems = map[string]subsystem{
20
-		"devices":    &DevicesGroup{},
21
-		"memory":     &MemoryGroup{},
22
-		"cpu":        &CpuGroup{},
23
-		"cpuset":     &CpusetGroup{},
24
-		"cpuacct":    &CpuacctGroup{},
25
-		"blkio":      &BlkioGroup{},
26
-		"hugetlb":    &HugetlbGroup{},
27
-		"net_cls":    &NetClsGroup{},
28
-		"net_prio":   &NetPrioGroup{},
29
-		"perf_event": &PerfEventGroup{},
30
-		"freezer":    &FreezerGroup{},
19
+	subsystems = subsystemSet{
20
+		&CpusetGroup{},
21
+		&DevicesGroup{},
22
+		&MemoryGroup{},
23
+		&CpuGroup{},
24
+		&CpuacctGroup{},
25
+		&BlkioGroup{},
26
+		&HugetlbGroup{},
27
+		&NetClsGroup{},
28
+		&NetPrioGroup{},
29
+		&PerfEventGroup{},
30
+		&FreezerGroup{},
31 31
 	}
32 32
 	CgroupProcesses  = "cgroup.procs"
33 33
 	HugePageSizes, _ = cgroups.GetHugePageSize()
34 34
 )
35 35
 
36
+var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")
37
+
38
+type subsystemSet []subsystem
39
+
40
+func (s subsystemSet) Get(name string) (subsystem, error) {
41
+	for _, ss := range s {
42
+		if ss.Name() == name {
43
+			return ss, nil
44
+		}
45
+	}
46
+	return nil, errSubsystemDoesNotExist
47
+}
48
+
36 49
 type subsystem interface {
50
+	// Name returns the name of the subsystem.
51
+	Name() string
37 52
 	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
38 53
 	GetStats(path string, stats *cgroups.Stats) error
39
-	// Removes the cgroup represented by 'data'.
40
-	Remove(*data) error
41
-	// Creates and joins the cgroup represented by data.
42
-	Apply(*data) error
54
+	// Removes the cgroup represented by 'cgroupData'.
55
+	Remove(*cgroupData) error
56
+	// Creates and joins the cgroup represented by 'cgroupData'.
57
+	Apply(*cgroupData) error
43 58
 	// Set the cgroup represented by cgroup.
44 59
 	Set(path string, cgroup *configs.Cgroup) error
45 60
 }
... ...
@@ -76,10 +92,11 @@ func getCgroupRoot() (string, error) {
76 76
 	return cgroupRoot, nil
77 77
 }
78 78
 
79
-type data struct {
79
+type cgroupData struct {
80 80
 	root   string
81
-	cgroup string
82
-	c      *configs.Cgroup
81
+	parent string
82
+	name   string
83
+	config *configs.Cgroup
83 84
 	pid    int
84 85
 }
85 86
 
... ...
@@ -101,21 +118,21 @@ func (m *Manager) Apply(pid int) (err error) {
101 101
 			cgroups.RemovePaths(paths)
102 102
 		}
103 103
 	}()
104
-	for name, sys := range subsystems {
104
+	for _, sys := range subsystems {
105 105
 		if err := sys.Apply(d); err != nil {
106 106
 			return err
107 107
 		}
108 108
 		// TODO: Apply should, ideally, be reentrant or be broken up into a separate
109 109
 		// create and join phase so that the cgroup hierarchy for a container can be
110 110
 		// created then join consists of writing the process pids to cgroup.procs
111
-		p, err := d.path(name)
111
+		p, err := d.path(sys.Name())
112 112
 		if err != nil {
113 113
 			if cgroups.IsNotFound(err) {
114 114
 				continue
115 115
 			}
116 116
 			return err
117 117
 		}
118
-		paths[name] = p
118
+		paths[sys.Name()] = p
119 119
 	}
120 120
 	m.Paths = paths
121 121
 
... ...
@@ -150,29 +167,27 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) {
150 150
 	defer m.mu.Unlock()
151 151
 	stats := cgroups.NewStats()
152 152
 	for name, path := range m.Paths {
153
-		sys, ok := subsystems[name]
154
-		if !ok || !cgroups.PathExists(path) {
153
+		sys, err := subsystems.Get(name)
154
+		if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) {
155 155
 			continue
156 156
 		}
157 157
 		if err := sys.GetStats(path, stats); err != nil {
158 158
 			return nil, err
159 159
 		}
160 160
 	}
161
-
162 161
 	return stats, nil
163 162
 }
164 163
 
165 164
 func (m *Manager) Set(container *configs.Config) error {
166 165
 	for name, path := range m.Paths {
167
-		sys, ok := subsystems[name]
168
-		if !ok || !cgroups.PathExists(path) {
166
+		sys, err := subsystems.Get(name)
167
+		if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) {
169 168
 			continue
170 169
 		}
171 170
 		if err := sys.Set(path, container.Cgroups); err != nil {
172 171
 			return err
173 172
 		}
174 173
 	}
175
-
176 174
 	return nil
177 175
 }
178 176
 
... ...
@@ -183,22 +198,21 @@ func (m *Manager) Freeze(state configs.FreezerState) error {
183 183
 	if err != nil {
184 184
 		return err
185 185
 	}
186
-
187 186
 	dir, err := d.path("freezer")
188 187
 	if err != nil {
189 188
 		return err
190 189
 	}
191
-
192 190
 	prevState := m.Cgroups.Freezer
193 191
 	m.Cgroups.Freezer = state
194
-
195
-	freezer := subsystems["freezer"]
192
+	freezer, err := subsystems.Get("freezer")
193
+	if err != nil {
194
+		return err
195
+	}
196 196
 	err = freezer.Set(dir, m.Cgroups)
197 197
 	if err != nil {
198 198
 		m.Cgroups.Freezer = prevState
199 199
 		return err
200 200
 	}
201
-
202 201
 	return nil
203 202
 }
204 203
 
... ...
@@ -216,30 +230,31 @@ func (m *Manager) GetPids() ([]int, error) {
216 216
 	return cgroups.GetPids(dir)
217 217
 }
218 218
 
219
-func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
219
+func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) {
220 220
 	root, err := getCgroupRoot()
221 221
 	if err != nil {
222 222
 		return nil, err
223 223
 	}
224 224
 
225
-	cgroup := c.Name
226
-	if c.Parent != "" {
227
-		cgroup = filepath.Join(c.Parent, cgroup)
228
-	}
229
-
230
-	return &data{
225
+	return &cgroupData{
231 226
 		root:   root,
232
-		cgroup: cgroup,
233
-		c:      c,
227
+		parent: c.Parent,
228
+		name:   c.Name,
229
+		config: c,
234 230
 		pid:    pid,
235 231
 	}, nil
236 232
 }
237 233
 
238
-func (raw *data) parent(subsystem, mountpoint, root string) (string, error) {
234
+func (raw *cgroupData) parentPath(subsystem, mountpoint, root string) (string, error) {
235
+	// Use GetThisCgroupDir instead of GetInitCgroupDir, because the creating
236
+	// process could in container and shared pid namespace with host, and
237
+	// /proc/1/cgroup could point to whole other world of cgroups.
239 238
 	initPath, err := cgroups.GetThisCgroupDir(subsystem)
240 239
 	if err != nil {
241 240
 		return "", err
242 241
 	}
242
+	// This is needed for nested containers, because in /proc/self/cgroup we
243
+	// see pathes from host, which don't exist in container.
243 244
 	relDir, err := filepath.Rel(root, initPath)
244 245
 	if err != nil {
245 246
 		return "", err
... ...
@@ -247,27 +262,29 @@ func (raw *data) parent(subsystem, mountpoint, root string) (string, error) {
247 247
 	return filepath.Join(mountpoint, relDir), nil
248 248
 }
249 249
 
250
-func (raw *data) path(subsystem string) (string, error) {
250
+func (raw *cgroupData) path(subsystem string) (string, error) {
251 251
 	mnt, root, err := cgroups.FindCgroupMountpointAndRoot(subsystem)
252 252
 	// If we didn't mount the subsystem, there is no point we make the path.
253 253
 	if err != nil {
254 254
 		return "", err
255 255
 	}
256 256
 
257
+	cgPath := filepath.Join(raw.parent, raw.name)
257 258
 	// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
258
-	if filepath.IsAbs(raw.cgroup) {
259
-		return filepath.Join(raw.root, filepath.Base(mnt), raw.cgroup), nil
259
+	if filepath.IsAbs(cgPath) {
260
+		// Sometimes subsystems can be mounted togethger as 'cpu,cpuacct'.
261
+		return filepath.Join(raw.root, filepath.Base(mnt), cgPath), nil
260 262
 	}
261 263
 
262
-	parent, err := raw.parent(subsystem, mnt, root)
264
+	parentPath, err := raw.parentPath(subsystem, mnt, root)
263 265
 	if err != nil {
264 266
 		return "", err
265 267
 	}
266 268
 
267
-	return filepath.Join(parent, raw.cgroup), nil
269
+	return filepath.Join(parentPath, cgPath), nil
268 270
 }
269 271
 
270
-func (raw *data) join(subsystem string) (string, error) {
272
+func (raw *cgroupData) join(subsystem string) (string, error) {
271 273
 	path, err := raw.path(subsystem)
272 274
 	if err != nil {
273 275
 		return "", err
... ...
@@ -17,13 +17,17 @@ import (
17 17
 type BlkioGroup struct {
18 18
 }
19 19
 
20
-func (s *BlkioGroup) Apply(d *data) error {
20
+func (s *BlkioGroup) Name() string {
21
+	return "blkio"
22
+}
23
+
24
+func (s *BlkioGroup) Apply(d *cgroupData) error {
21 25
 	dir, err := d.join("blkio")
22 26
 	if err != nil && !cgroups.IsNotFound(err) {
23 27
 		return err
24 28
 	}
25 29
 
26
-	if err := s.Set(dir, d.c); err != nil {
30
+	if err := s.Set(dir, d.config); err != nil {
27 31
 		return err
28 32
 	}
29 33
 
... ...
@@ -74,7 +78,7 @@ func (s *BlkioGroup) Set(path string, cgroup *configs.Cgroup) error {
74 74
 	return nil
75 75
 }
76 76
 
77
-func (s *BlkioGroup) Remove(d *data) error {
77
+func (s *BlkioGroup) Remove(d *cgroupData) error {
78 78
 	return removePath(d.path("blkio"))
79 79
 }
80 80
 
... ...
@@ -15,7 +15,11 @@ import (
15 15
 type CpuGroup struct {
16 16
 }
17 17
 
18
-func (s *CpuGroup) Apply(d *data) error {
18
+func (s *CpuGroup) Name() string {
19
+	return "cpu"
20
+}
21
+
22
+func (s *CpuGroup) Apply(d *cgroupData) error {
19 23
 	// We always want to join the cpu group, to allow fair cpu scheduling
20 24
 	// on a container basis
21 25
 	dir, err := d.join("cpu")
... ...
@@ -23,7 +27,7 @@ func (s *CpuGroup) Apply(d *data) error {
23 23
 		return err
24 24
 	}
25 25
 
26
-	if err := s.Set(dir, d.c); err != nil {
26
+	if err := s.Set(dir, d.config); err != nil {
27 27
 		return err
28 28
 	}
29 29
 
... ...
@@ -60,7 +64,7 @@ func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error {
60 60
 	return nil
61 61
 }
62 62
 
63
-func (s *CpuGroup) Remove(d *data) error {
63
+func (s *CpuGroup) Remove(d *cgroupData) error {
64 64
 	return removePath(d.path("cpu"))
65 65
 }
66 66
 
... ...
@@ -24,7 +24,11 @@ var clockTicks = uint64(system.GetClockTicks())
24 24
 type CpuacctGroup struct {
25 25
 }
26 26
 
27
-func (s *CpuacctGroup) Apply(d *data) error {
27
+func (s *CpuacctGroup) Name() string {
28
+	return "cpuacct"
29
+}
30
+
31
+func (s *CpuacctGroup) Apply(d *cgroupData) error {
28 32
 	// we just want to join this group even though we don't set anything
29 33
 	if _, err := d.join("cpuacct"); err != nil && !cgroups.IsNotFound(err) {
30 34
 		return err
... ...
@@ -37,7 +41,7 @@ func (s *CpuacctGroup) Set(path string, cgroup *configs.Cgroup) error {
37 37
 	return nil
38 38
 }
39 39
 
40
-func (s *CpuacctGroup) Remove(d *data) error {
40
+func (s *CpuacctGroup) Remove(d *cgroupData) error {
41 41
 	return removePath(d.path("cpuacct"))
42 42
 }
43 43
 
... ...
@@ -16,12 +16,16 @@ import (
16 16
 type CpusetGroup struct {
17 17
 }
18 18
 
19
-func (s *CpusetGroup) Apply(d *data) error {
19
+func (s *CpusetGroup) Name() string {
20
+	return "cpuset"
21
+}
22
+
23
+func (s *CpusetGroup) Apply(d *cgroupData) error {
20 24
 	dir, err := d.path("cpuset")
21 25
 	if err != nil && !cgroups.IsNotFound(err) {
22 26
 		return err
23 27
 	}
24
-	return s.ApplyDir(dir, d.c, d.pid)
28
+	return s.ApplyDir(dir, d.config, d.pid)
25 29
 }
26 30
 
27 31
 func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error {
... ...
@@ -38,7 +42,7 @@ func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error {
38 38
 	return nil
39 39
 }
40 40
 
41
-func (s *CpusetGroup) Remove(d *data) error {
41
+func (s *CpusetGroup) Remove(d *cgroupData) error {
42 42
 	return removePath(d.path("cpuset"))
43 43
 }
44 44
 
... ...
@@ -59,17 +63,16 @@ func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) erro
59 59
 	if err := s.ensureParent(dir, root); err != nil {
60 60
 		return err
61 61
 	}
62
-	// because we are not using d.join we need to place the pid into the procs file
63
-	// unlike the other subsystems
64
-	if err := writeFile(dir, "cgroup.procs", strconv.Itoa(pid)); err != nil {
65
-		return err
66
-	}
67
-
68 62
 	// the default values inherit from parent cgroup are already set in
69 63
 	// s.ensureParent, cover these if we have our own
70 64
 	if err := s.Set(dir, cgroup); err != nil {
71 65
 		return err
72 66
 	}
67
+	// because we are not using d.join we need to place the pid into the procs file
68
+	// unlike the other subsystems
69
+	if err := writeFile(dir, "cgroup.procs", strconv.Itoa(pid)); err != nil {
70
+		return err
71
+	}
73 72
 
74 73
 	return nil
75 74
 }
... ...
@@ -10,7 +10,11 @@ import (
10 10
 type DevicesGroup struct {
11 11
 }
12 12
 
13
-func (s *DevicesGroup) Apply(d *data) error {
13
+func (s *DevicesGroup) Name() string {
14
+	return "devices"
15
+}
16
+
17
+func (s *DevicesGroup) Apply(d *cgroupData) error {
14 18
 	dir, err := d.join("devices")
15 19
 	if err != nil {
16 20
 		// We will return error even it's `not found` error, devices
... ...
@@ -18,7 +22,7 @@ func (s *DevicesGroup) Apply(d *data) error {
18 18
 		return err
19 19
 	}
20 20
 
21
-	if err := s.Set(dir, d.c); err != nil {
21
+	if err := s.Set(dir, d.config); err != nil {
22 22
 		return err
23 23
 	}
24 24
 
... ...
@@ -52,7 +56,7 @@ func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
52 52
 	return nil
53 53
 }
54 54
 
55
-func (s *DevicesGroup) Remove(d *data) error {
55
+func (s *DevicesGroup) Remove(d *cgroupData) error {
56 56
 	return removePath(d.path("devices"))
57 57
 }
58 58
 
... ...
@@ -14,13 +14,17 @@ import (
14 14
 type FreezerGroup struct {
15 15
 }
16 16
 
17
-func (s *FreezerGroup) Apply(d *data) error {
17
+func (s *FreezerGroup) Name() string {
18
+	return "freezer"
19
+}
20
+
21
+func (s *FreezerGroup) Apply(d *cgroupData) error {
18 22
 	dir, err := d.join("freezer")
19 23
 	if err != nil && !cgroups.IsNotFound(err) {
20 24
 		return err
21 25
 	}
22 26
 
23
-	if err := s.Set(dir, d.c); err != nil {
27
+	if err := s.Set(dir, d.config); err != nil {
24 28
 		return err
25 29
 	}
26 30
 
... ...
@@ -53,7 +57,7 @@ func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error {
53 53
 	return nil
54 54
 }
55 55
 
56
-func (s *FreezerGroup) Remove(d *data) error {
56
+func (s *FreezerGroup) Remove(d *cgroupData) error {
57 57
 	return removePath(d.path("freezer"))
58 58
 }
59 59
 
... ...
@@ -14,13 +14,17 @@ import (
14 14
 type HugetlbGroup struct {
15 15
 }
16 16
 
17
-func (s *HugetlbGroup) Apply(d *data) error {
17
+func (s *HugetlbGroup) Name() string {
18
+	return "hugetlb"
19
+}
20
+
21
+func (s *HugetlbGroup) Apply(d *cgroupData) error {
18 22
 	dir, err := d.join("hugetlb")
19 23
 	if err != nil && !cgroups.IsNotFound(err) {
20 24
 		return err
21 25
 	}
22 26
 
23
-	if err := s.Set(dir, d.c); err != nil {
27
+	if err := s.Set(dir, d.config); err != nil {
24 28
 		return err
25 29
 	}
26 30
 
... ...
@@ -37,7 +41,7 @@ func (s *HugetlbGroup) Set(path string, cgroup *configs.Cgroup) error {
37 37
 	return nil
38 38
 }
39 39
 
40
-func (s *HugetlbGroup) Remove(d *data) error {
40
+func (s *HugetlbGroup) Remove(d *cgroupData) error {
41 41
 	return removePath(d.path("hugetlb"))
42 42
 }
43 43
 
... ...
@@ -17,16 +17,25 @@ import (
17 17
 type MemoryGroup struct {
18 18
 }
19 19
 
20
-func (s *MemoryGroup) Apply(d *data) (err error) {
20
+func (s *MemoryGroup) Name() string {
21
+	return "memory"
22
+}
23
+
24
+func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
21 25
 	path, err := d.path("memory")
22
-	if err != nil {
23
-		if cgroups.IsNotFound(err) {
24
-			return nil
25
-		}
26
+	if err != nil && !cgroups.IsNotFound(err) {
26 27
 		return err
27 28
 	}
28
-	if err := os.MkdirAll(path, 0755); err != nil {
29
-		return err
29
+	if memoryAssigned(d.config) {
30
+		if path != "" {
31
+			if err := os.MkdirAll(path, 0755); err != nil {
32
+				return err
33
+			}
34
+		}
35
+
36
+		if err := s.Set(path, d.config); err != nil {
37
+			return err
38
+		}
30 39
 	}
31 40
 
32 41
 	defer func() {
... ...
@@ -35,13 +44,10 @@ func (s *MemoryGroup) Apply(d *data) (err error) {
35 35
 		}
36 36
 	}()
37 37
 
38
-	if err := s.Set(path, d.c); err != nil {
39
-		return err
40
-	}
41
-
42 38
 	// We need to join memory cgroup after set memory limits, because
43 39
 	// kmem.limit_in_bytes can only be set when the cgroup is empty.
44
-	if _, err = d.join("memory"); err != nil {
40
+	_, err = d.join("memory")
41
+	if err != nil && !cgroups.IsNotFound(err) {
45 42
 		return err
46 43
 	}
47 44
 
... ...
@@ -88,7 +94,7 @@ func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error {
88 88
 	return nil
89 89
 }
90 90
 
91
-func (s *MemoryGroup) Remove(d *data) error {
91
+func (s *MemoryGroup) Remove(d *cgroupData) error {
92 92
 	return removePath(d.path("memory"))
93 93
 }
94 94
 
... ...
@@ -132,6 +138,15 @@ func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error {
132 132
 	return nil
133 133
 }
134 134
 
135
+func memoryAssigned(cgroup *configs.Cgroup) bool {
136
+	return cgroup.Memory != 0 ||
137
+		cgroup.MemoryReservation != 0 ||
138
+		cgroup.MemorySwap > 0 ||
139
+		cgroup.KernelMemory > 0 ||
140
+		cgroup.OomKillDisable ||
141
+		cgroup.MemorySwappiness != -1
142
+}
143
+
135 144
 func getMemoryData(path, name string) (cgroups.MemoryData, error) {
136 145
 	memoryData := cgroups.MemoryData{}
137 146
 
... ...
@@ -1,3 +1,5 @@
1
+// +build linux
2
+
1 3
 package fs
2 4
 
3 5
 import (
... ...
@@ -6,9 +8,14 @@ import (
6 6
 )
7 7
 
8 8
 type NameGroup struct {
9
+	GroupName string
10
+}
11
+
12
+func (s *NameGroup) Name() string {
13
+	return s.GroupName
9 14
 }
10 15
 
11
-func (s *NameGroup) Apply(d *data) error {
16
+func (s *NameGroup) Apply(d *cgroupData) error {
12 17
 	return nil
13 18
 }
14 19
 
... ...
@@ -16,7 +23,7 @@ func (s *NameGroup) Set(path string, cgroup *configs.Cgroup) error {
16 16
 	return nil
17 17
 }
18 18
 
19
-func (s *NameGroup) Remove(d *data) error {
19
+func (s *NameGroup) Remove(d *cgroupData) error {
20 20
 	return nil
21 21
 }
22 22
 
... ...
@@ -1,3 +1,5 @@
1
+// +build linux
2
+
1 3
 package fs
2 4
 
3 5
 import (
... ...
@@ -8,13 +10,17 @@ import (
8 8
 type NetClsGroup struct {
9 9
 }
10 10
 
11
-func (s *NetClsGroup) Apply(d *data) error {
11
+func (s *NetClsGroup) Name() string {
12
+	return "net_cls"
13
+}
14
+
15
+func (s *NetClsGroup) Apply(d *cgroupData) error {
12 16
 	dir, err := d.join("net_cls")
13 17
 	if err != nil && !cgroups.IsNotFound(err) {
14 18
 		return err
15 19
 	}
16 20
 
17
-	if err := s.Set(dir, d.c); err != nil {
21
+	if err := s.Set(dir, d.config); err != nil {
18 22
 		return err
19 23
 	}
20 24
 
... ...
@@ -31,7 +37,7 @@ func (s *NetClsGroup) Set(path string, cgroup *configs.Cgroup) error {
31 31
 	return nil
32 32
 }
33 33
 
34
-func (s *NetClsGroup) Remove(d *data) error {
34
+func (s *NetClsGroup) Remove(d *cgroupData) error {
35 35
 	return removePath(d.path("net_cls"))
36 36
 }
37 37
 
... ...
@@ -1,3 +1,5 @@
1
+// +build linux
2
+
1 3
 package fs
2 4
 
3 5
 import (
... ...
@@ -8,13 +10,17 @@ import (
8 8
 type NetPrioGroup struct {
9 9
 }
10 10
 
11
-func (s *NetPrioGroup) Apply(d *data) error {
11
+func (s *NetPrioGroup) Name() string {
12
+	return "net_prio"
13
+}
14
+
15
+func (s *NetPrioGroup) Apply(d *cgroupData) error {
12 16
 	dir, err := d.join("net_prio")
13 17
 	if err != nil && !cgroups.IsNotFound(err) {
14 18
 		return err
15 19
 	}
16 20
 
17
-	if err := s.Set(dir, d.c); err != nil {
21
+	if err := s.Set(dir, d.config); err != nil {
18 22
 		return err
19 23
 	}
20 24
 
... ...
@@ -31,7 +37,7 @@ func (s *NetPrioGroup) Set(path string, cgroup *configs.Cgroup) error {
31 31
 	return nil
32 32
 }
33 33
 
34
-func (s *NetPrioGroup) Remove(d *data) error {
34
+func (s *NetPrioGroup) Remove(d *cgroupData) error {
35 35
 	return removePath(d.path("net_prio"))
36 36
 }
37 37
 
... ...
@@ -10,7 +10,11 @@ import (
10 10
 type PerfEventGroup struct {
11 11
 }
12 12
 
13
-func (s *PerfEventGroup) Apply(d *data) error {
13
+func (s *PerfEventGroup) Name() string {
14
+	return "perf_event"
15
+}
16
+
17
+func (s *PerfEventGroup) Apply(d *cgroupData) error {
14 18
 	// we just want to join this group even though we don't set anything
15 19
 	if _, err := d.join("perf_event"); err != nil && !cgroups.IsNotFound(err) {
16 20
 		return err
... ...
@@ -22,7 +26,7 @@ func (s *PerfEventGroup) Set(path string, cgroup *configs.Cgroup) error {
22 22
 	return nil
23 23
 }
24 24
 
25
-func (s *PerfEventGroup) Remove(d *data) error {
25
+func (s *PerfEventGroup) Remove(d *cgroupData) error {
26 26
 	return removePath(d.path("perf_event"))
27 27
 }
28 28
 
... ...
@@ -44,7 +44,7 @@ func getCgroupParamKeyValue(t string) (string, uint64, error) {
44 44
 	case 2:
45 45
 		value, err := parseUint(parts[1], 10, 64)
46 46
 		if err != nil {
47
-			return "", 0, fmt.Errorf("Unable to convert param value (%q) to uint64: %v", parts[1], err)
47
+			return "", 0, fmt.Errorf("unable to convert param value (%q) to uint64: %v", parts[1], err)
48 48
 		}
49 49
 
50 50
 		return parts[0], value, nil
... ...
@@ -55,12 +55,17 @@ func getCgroupParamKeyValue(t string) (string, uint64, error) {
55 55
 
56 56
 // Gets a single uint64 value from the specified cgroup file.
57 57
 func getCgroupParamUint(cgroupPath, cgroupFile string) (uint64, error) {
58
-	contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile))
58
+	fileName := filepath.Join(cgroupPath, cgroupFile)
59
+	contents, err := ioutil.ReadFile(fileName)
59 60
 	if err != nil {
60 61
 		return 0, err
61 62
 	}
62 63
 
63
-	return parseUint(strings.TrimSpace(string(contents)), 10, 64)
64
+	res, err := parseUint(strings.TrimSpace(string(contents)), 10, 64)
65
+	if err != nil {
66
+		return res, fmt.Errorf("unable to parse %q as a uint from Cgroup file %q", string(contents), fileName)
67
+	}
68
+	return res, nil
64 69
 }
65 70
 
66 71
 // Gets a string value from the specified cgroup file
... ...
@@ -3,6 +3,7 @@
3 3
 package systemd
4 4
 
5 5
 import (
6
+	"errors"
6 7
 	"fmt"
7 8
 	"io/ioutil"
8 9
 	"os"
... ...
@@ -27,25 +28,40 @@ type Manager struct {
27 27
 }
28 28
 
29 29
 type subsystem interface {
30
+	// Name returns the name of the subsystem.
31
+	Name() string
30 32
 	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
31 33
 	GetStats(path string, stats *cgroups.Stats) error
32 34
 	// Set the cgroup represented by cgroup.
33 35
 	Set(path string, cgroup *configs.Cgroup) error
34 36
 }
35 37
 
36
-var subsystems = map[string]subsystem{
37
-	"devices":      &fs.DevicesGroup{},
38
-	"memory":       &fs.MemoryGroup{},
39
-	"cpu":          &fs.CpuGroup{},
40
-	"cpuset":       &fs.CpusetGroup{},
41
-	"cpuacct":      &fs.CpuacctGroup{},
42
-	"blkio":        &fs.BlkioGroup{},
43
-	"hugetlb":      &fs.HugetlbGroup{},
44
-	"perf_event":   &fs.PerfEventGroup{},
45
-	"freezer":      &fs.FreezerGroup{},
46
-	"net_prio":     &fs.NetPrioGroup{},
47
-	"net_cls":      &fs.NetClsGroup{},
48
-	"name=systemd": &fs.NameGroup{},
38
+var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")
39
+
40
+type subsystemSet []subsystem
41
+
42
+func (s subsystemSet) Get(name string) (subsystem, error) {
43
+	for _, ss := range s {
44
+		if ss.Name() == name {
45
+			return ss, nil
46
+		}
47
+	}
48
+	return nil, errSubsystemDoesNotExist
49
+}
50
+
51
+var subsystems = subsystemSet{
52
+	&fs.CpusetGroup{},
53
+	&fs.DevicesGroup{},
54
+	&fs.MemoryGroup{},
55
+	&fs.CpuGroup{},
56
+	&fs.CpuacctGroup{},
57
+	&fs.BlkioGroup{},
58
+	&fs.HugetlbGroup{},
59
+	&fs.PerfEventGroup{},
60
+	&fs.FreezerGroup{},
61
+	&fs.NetPrioGroup{},
62
+	&fs.NetClsGroup{},
63
+	&fs.NameGroup{GroupName: "name=systemd"},
49 64
 }
50 65
 
51 66
 const (
... ...
@@ -249,8 +265,8 @@ func (m *Manager) Apply(pid int) error {
249 249
 	}
250 250
 
251 251
 	paths := make(map[string]string)
252
-	for sysname := range subsystems {
253
-		subsystemPath, err := getSubsystemPath(m.Cgroups, sysname)
252
+	for _, s := range subsystems {
253
+		subsystemPath, err := getSubsystemPath(m.Cgroups, s.Name())
254 254
 		if err != nil {
255 255
 			// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
256 256
 			if cgroups.IsNotFound(err) {
... ...
@@ -258,7 +274,7 @@ func (m *Manager) Apply(pid int) error {
258 258
 			}
259 259
 			return err
260 260
 		}
261
-		paths[sysname] = subsystemPath
261
+		paths[s.Name()] = subsystemPath
262 262
 	}
263 263
 	m.Paths = paths
264 264
 
... ...
@@ -347,8 +363,10 @@ func joinFreezer(c *configs.Cgroup, pid int) error {
347 347
 	if err != nil && !cgroups.IsNotFound(err) {
348 348
 		return err
349 349
 	}
350
-
351
-	freezer := subsystems["freezer"]
350
+	freezer, err := subsystems.Get("freezer")
351
+	if err != nil {
352
+		return err
353
+	}
352 354
 	return freezer.Set(path, c)
353 355
 }
354 356
 
... ...
@@ -357,8 +375,10 @@ func joinNetPrio(c *configs.Cgroup, pid int) error {
357 357
 	if err != nil && !cgroups.IsNotFound(err) {
358 358
 		return err
359 359
 	}
360
-	netPrio := subsystems["net_prio"]
361
-
360
+	netPrio, err := subsystems.Get("net_prio")
361
+	if err != nil {
362
+		return err
363
+	}
362 364
 	return netPrio.Set(path, c)
363 365
 }
364 366
 
... ...
@@ -367,8 +387,10 @@ func joinNetCls(c *configs.Cgroup, pid int) error {
367 367
 	if err != nil && !cgroups.IsNotFound(err) {
368 368
 		return err
369 369
 	}
370
-	netcls := subsystems["net_cls"]
371
-
370
+	netcls, err := subsystems.Get("net_cls")
371
+	if err != nil {
372
+		return err
373
+	}
372 374
 	return netcls.Set(path, c)
373 375
 }
374 376
 
... ...
@@ -396,17 +418,17 @@ func (m *Manager) Freeze(state configs.FreezerState) error {
396 396
 	if err != nil {
397 397
 		return err
398 398
 	}
399
-
400 399
 	prevState := m.Cgroups.Freezer
401 400
 	m.Cgroups.Freezer = state
402
-
403
-	freezer := subsystems["freezer"]
401
+	freezer, err := subsystems.Get("freezer")
402
+	if err != nil {
403
+		return err
404
+	}
404 405
 	err = freezer.Set(path, m.Cgroups)
405 406
 	if err != nil {
406 407
 		m.Cgroups.Freezer = prevState
407 408
 		return err
408 409
 	}
409
-
410 410
 	return nil
411 411
 }
412 412
 
... ...
@@ -423,8 +445,8 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) {
423 423
 	defer m.mu.Unlock()
424 424
 	stats := cgroups.NewStats()
425 425
 	for name, path := range m.Paths {
426
-		sys, ok := subsystems[name]
427
-		if !ok || !cgroups.PathExists(path) {
426
+		sys, err := subsystems.Get(name)
427
+		if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) {
428 428
 			continue
429 429
 		}
430 430
 		if err := sys.GetStats(path, stats); err != nil {
... ...
@@ -437,8 +459,8 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) {
437 437
 
438 438
 func (m *Manager) Set(container *configs.Config) error {
439 439
 	for name, path := range m.Paths {
440
-		sys, ok := subsystems[name]
441
-		if !ok || !cgroups.PathExists(path) {
440
+		sys, err := subsystems.Get(name)
441
+		if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) {
442 442
 			continue
443 443
 		}
444 444
 		if err := sys.Set(path, container.Cgroups); err != nil {
... ...
@@ -471,8 +493,10 @@ func joinDevices(c *configs.Cgroup, pid int) error {
471 471
 	if err != nil {
472 472
 		return err
473 473
 	}
474
-
475
-	devices := subsystems["devices"]
474
+	devices, err := subsystems.Get("devices")
475
+	if err != nil {
476
+		return err
477
+	}
476 478
 	return devices.Set(path, c)
477 479
 }
478 480
 
... ...
@@ -600,8 +624,10 @@ func joinHugetlb(c *configs.Cgroup, pid int) error {
600 600
 	if err != nil && !cgroups.IsNotFound(err) {
601 601
 		return err
602 602
 	}
603
-
604
-	hugetlb := subsystems["hugetlb"]
603
+	hugetlb, err := subsystems.Get("hugetlb")
604
+	if err != nil {
605
+		return err
606
+	}
605 607
 	return hugetlb.Set(path, c)
606 608
 }
607 609
 
... ...
@@ -610,7 +636,9 @@ func joinPerfEvent(c *configs.Cgroup, pid int) error {
610 610
 	if err != nil && !cgroups.IsNotFound(err) {
611 611
 		return err
612 612
 	}
613
-
614
-	perfEvent := subsystems["perf_event"]
613
+	perfEvent, err := subsystems.Get("perf_event")
614
+	if err != nil {
615
+		return err
616
+	}
615 617
 	return perfEvent.Set(path, c)
616 618
 }
617 619
deleted file mode 100644
... ...
@@ -1,101 +0,0 @@
1
-package configs
2
-
3
-type FreezerState string
4
-
5
-const (
6
-	Undefined FreezerState = ""
7
-	Frozen    FreezerState = "FROZEN"
8
-	Thawed    FreezerState = "THAWED"
9
-)
10
-
11
-// TODO Windows: This can be factored out in the future as Cgroups are not
12
-// supported on the Windows platform.
13
-
14
-type Cgroup struct {
15
-	Name string `json:"name"`
16
-
17
-	// name of parent cgroup or slice
18
-	Parent string `json:"parent"`
19
-
20
-	// If this is true allow access to any kind of device within the container.  If false, allow access only to devices explicitly listed in the allowed_devices list.
21
-	AllowAllDevices bool `json:"allow_all_devices"`
22
-
23
-	AllowedDevices []*Device `json:"allowed_devices"`
24
-
25
-	DeniedDevices []*Device `json:"denied_devices"`
26
-
27
-	// Memory limit (in bytes)
28
-	Memory int64 `json:"memory"`
29
-
30
-	// Memory reservation or soft_limit (in bytes)
31
-	MemoryReservation int64 `json:"memory_reservation"`
32
-
33
-	// Total memory usage (memory + swap); set `-1' to disable swap
34
-	MemorySwap int64 `json:"memory_swap"`
35
-
36
-	// Kernel memory limit (in bytes)
37
-	KernelMemory int64 `json:"kernel_memory"`
38
-
39
-	// CPU shares (relative weight vs. other containers)
40
-	CpuShares int64 `json:"cpu_shares"`
41
-
42
-	// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
43
-	CpuQuota int64 `json:"cpu_quota"`
44
-
45
-	// CPU period to be used for hardcapping (in usecs). 0 to use system default.
46
-	CpuPeriod int64 `json:"cpu_period"`
47
-
48
-	// How many time CPU will use in realtime scheduling (in usecs).
49
-	CpuRtRuntime int64 `json:"cpu_quota"`
50
-
51
-	// CPU period to be used for realtime scheduling (in usecs).
52
-	CpuRtPeriod int64 `json:"cpu_period"`
53
-
54
-	// CPU to use
55
-	CpusetCpus string `json:"cpuset_cpus"`
56
-
57
-	// MEM to use
58
-	CpusetMems string `json:"cpuset_mems"`
59
-
60
-	// Specifies per cgroup weight, range is from 10 to 1000.
61
-	BlkioWeight uint16 `json:"blkio_weight"`
62
-
63
-	// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only
64
-	BlkioLeafWeight uint16 `json:"blkio_leaf_weight"`
65
-
66
-	// Weight per cgroup per device, can override BlkioWeight.
67
-	BlkioWeightDevice []*WeightDevice `json:"blkio_weight_device"`
68
-
69
-	// IO read rate limit per cgroup per device, bytes per second.
70
-	BlkioThrottleReadBpsDevice []*ThrottleDevice `json:"blkio_throttle_read_bps_device"`
71
-
72
-	// IO write rate limit per cgroup per divice, bytes per second.
73
-	BlkioThrottleWriteBpsDevice []*ThrottleDevice `json:"blkio_throttle_write_bps_device"`
74
-
75
-	// IO read rate limit per cgroup per device, IO per second.
76
-	BlkioThrottleReadIOPSDevice []*ThrottleDevice `json:"blkio_throttle_read_iops_device"`
77
-
78
-	// IO write rate limit per cgroup per device, IO per second.
79
-	BlkioThrottleWriteIOPSDevice []*ThrottleDevice `json:"blkio_throttle_write_iops_device"`
80
-
81
-	// set the freeze value for the process
82
-	Freezer FreezerState `json:"freezer"`
83
-
84
-	// Hugetlb limit (in bytes)
85
-	HugetlbLimit []*HugepageLimit `json:"hugetlb_limit"`
86
-
87
-	// Parent slice to use for systemd TODO: remove in favor or parent
88
-	Slice string `json:"slice"`
89
-
90
-	// Whether to disable OOM Killer
91
-	OomKillDisable bool `json:"oom_kill_disable"`
92
-
93
-	// Tuning swappiness behaviour per cgroup
94
-	MemorySwappiness int64 `json:"memory_swappiness"`
95
-
96
-	// Set priority of network traffic for container
97
-	NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap"`
98
-
99
-	// Set class identifier for container's network packets
100
-	NetClsClassid string `json:"net_cls_classid"`
101
-}
102 1
new file mode 100644
... ...
@@ -0,0 +1,100 @@
0
+// +build linux freebsd
1
+
2
+package configs
3
+
4
+type FreezerState string
5
+
6
+const (
7
+	Undefined FreezerState = ""
8
+	Frozen    FreezerState = "FROZEN"
9
+	Thawed    FreezerState = "THAWED"
10
+)
11
+
12
+type Cgroup struct {
13
+	Name string `json:"name"`
14
+
15
+	// name of parent cgroup or slice
16
+	Parent string `json:"parent"`
17
+
18
+	// If this is true allow access to any kind of device within the container.  If false, allow access only to devices explicitly listed in the allowed_devices list.
19
+	AllowAllDevices bool `json:"allow_all_devices"`
20
+
21
+	AllowedDevices []*Device `json:"allowed_devices"`
22
+
23
+	DeniedDevices []*Device `json:"denied_devices"`
24
+
25
+	// Memory limit (in bytes)
26
+	Memory int64 `json:"memory"`
27
+
28
+	// Memory reservation or soft_limit (in bytes)
29
+	MemoryReservation int64 `json:"memory_reservation"`
30
+
31
+	// Total memory usage (memory + swap); set `-1' to disable swap
32
+	MemorySwap int64 `json:"memory_swap"`
33
+
34
+	// Kernel memory limit (in bytes)
35
+	KernelMemory int64 `json:"kernel_memory"`
36
+
37
+	// CPU shares (relative weight vs. other containers)
38
+	CpuShares int64 `json:"cpu_shares"`
39
+
40
+	// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
41
+	CpuQuota int64 `json:"cpu_quota"`
42
+
43
+	// CPU period to be used for hardcapping (in usecs). 0 to use system default.
44
+	CpuPeriod int64 `json:"cpu_period"`
45
+
46
+	// How many time CPU will use in realtime scheduling (in usecs).
47
+	CpuRtRuntime int64 `json:"cpu_quota"`
48
+
49
+	// CPU period to be used for realtime scheduling (in usecs).
50
+	CpuRtPeriod int64 `json:"cpu_period"`
51
+
52
+	// CPU to use
53
+	CpusetCpus string `json:"cpuset_cpus"`
54
+
55
+	// MEM to use
56
+	CpusetMems string `json:"cpuset_mems"`
57
+
58
+	// Specifies per cgroup weight, range is from 10 to 1000.
59
+	BlkioWeight uint16 `json:"blkio_weight"`
60
+
61
+	// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only
62
+	BlkioLeafWeight uint16 `json:"blkio_leaf_weight"`
63
+
64
+	// Weight per cgroup per device, can override BlkioWeight.
65
+	BlkioWeightDevice []*WeightDevice `json:"blkio_weight_device"`
66
+
67
+	// IO read rate limit per cgroup per device, bytes per second.
68
+	BlkioThrottleReadBpsDevice []*ThrottleDevice `json:"blkio_throttle_read_bps_device"`
69
+
70
+	// IO write rate limit per cgroup per divice, bytes per second.
71
+	BlkioThrottleWriteBpsDevice []*ThrottleDevice `json:"blkio_throttle_write_bps_device"`
72
+
73
+	// IO read rate limit per cgroup per device, IO per second.
74
+	BlkioThrottleReadIOPSDevice []*ThrottleDevice `json:"blkio_throttle_read_iops_device"`
75
+
76
+	// IO write rate limit per cgroup per device, IO per second.
77
+	BlkioThrottleWriteIOPSDevice []*ThrottleDevice `json:"blkio_throttle_write_iops_device"`
78
+
79
+	// set the freeze value for the process
80
+	Freezer FreezerState `json:"freezer"`
81
+
82
+	// Hugetlb limit (in bytes)
83
+	HugetlbLimit []*HugepageLimit `json:"hugetlb_limit"`
84
+
85
+	// Parent slice to use for systemd TODO: remove in favor or parent
86
+	Slice string `json:"slice"`
87
+
88
+	// Whether to disable OOM Killer
89
+	OomKillDisable bool `json:"oom_kill_disable"`
90
+
91
+	// Tuning swappiness behaviour per cgroup
92
+	MemorySwappiness int64 `json:"memory_swappiness"`
93
+
94
+	// Set priority of network traffic for container
95
+	NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap"`
96
+
97
+	// Set class identifier for container's network packets
98
+	NetClsClassid string `json:"net_cls_classid"`
99
+}
0 100
new file mode 100644
... ...
@@ -0,0 +1,6 @@
0
+package configs
1
+
2
+// TODO Windows: This can ultimately be entirely factored out on Windows as
3
+// cgroups are a Unix-specific construct.
4
+type Cgroup struct {
5
+}
... ...
@@ -33,17 +33,18 @@ type Seccomp struct {
33 33
 type Action int
34 34
 
35 35
 const (
36
-	Kill Action = iota - 4
36
+	Kill Action = iota + 1
37 37
 	Errno
38 38
 	Trap
39 39
 	Allow
40
+	Trace
40 41
 )
41 42
 
42 43
 // A comparison operator to be used when matching syscall arguments in Seccomp
43 44
 type Operator int
44 45
 
45 46
 const (
46
-	EqualTo Operator = iota
47
+	EqualTo Operator = iota + 1
47 48
 	NotEqualTo
48 49
 	GreaterThan
49 50
 	GreaterThanOrEqualTo
... ...
@@ -183,6 +184,9 @@ type Hooks struct {
183 183
 	// but before the user supplied command is executed from init.
184 184
 	Prestart []Hook
185 185
 
186
+	// Poststart commands are executed after the container init process starts.
187
+	Poststart []Hook
188
+
186 189
 	// Poststop commands are executed after the container init process exits.
187 190
 	Poststop []Hook
188 191
 }
189 192
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+// +build !linux,!freebsd
1
+
2
+package configs
3
+
4
+// Namespace defines configuration for each namespace.  It specifies an
5
+// alternate path that is able to be joined via setns.
6
+type Namespace struct {
7
+}
0 8
deleted file mode 100644
... ...
@@ -1,6 +0,0 @@
1
-package configs
2
-
3
-// Namespace defines configuration for each namespace.  It specifies an
4
-// alternate path that is able to be joined via setns.
5
-type Namespace struct {
6
-}
... ...
@@ -30,8 +30,9 @@ const (
30 30
 	Destroyed
31 31
 )
32 32
 
33
-// State represents a running container's state
34
-type State struct {
33
+// BaseState represents the platform agnostic pieces relating to a
34
+// running container's state
35
+type BaseState struct {
35 36
 	// ID is the container ID.
36 37
 	ID string `json:"id"`
37 38
 
... ...
@@ -41,27 +42,16 @@ type State struct {
41 41
 	// InitProcessStartTime is the init process start time.
42 42
 	InitProcessStartTime string `json:"init_process_start"`
43 43
 
44
-	// Path to all the cgroups setup for a container. Key is cgroup subsystem name
45
-	// with the value as the path.
46
-	CgroupPaths map[string]string `json:"cgroup_paths"`
47
-
48
-	// NamespacePaths are filepaths to the container's namespaces. Key is the namespace type
49
-	// with the value as the path.
50
-	NamespacePaths map[configs.NamespaceType]string `json:"namespace_paths"`
51
-
52 44
 	// Config is the container's configuration.
53 45
 	Config configs.Config `json:"config"`
54
-
55
-	// Container's standard descriptors (std{in,out,err}), needed for checkpoint and restore
56
-	ExternalDescriptors []string `json:"external_descriptors,omitempty"`
57 46
 }
58 47
 
59 48
 // A libcontainer container object.
60 49
 //
61 50
 // Each container is thread-safe within the same process. Since a container can
62 51
 // be destroyed by a separate process, any function may return that the container
63
-// was not found.
64
-type Container interface {
52
+// was not found. BaseContainer includes methods that are platform agnostic.
53
+type BaseContainer interface {
65 54
 	// Returns the ID of the container
66 55
 	ID() string
67 56
 
... ...
@@ -98,7 +88,7 @@ type Container interface {
98 98
 	// Systemerror - System error.
99 99
 	Stats() (*Stats, error)
100 100
 
101
-	// Set cgroup resources of container as configured
101
+	// Set resources of container as configured
102 102
 	//
103 103
 	// We can use this to change resources when containers are running.
104 104
 	//
... ...
@@ -116,18 +106,6 @@ type Container interface {
116 116
 	// Systemerror - System error.
117 117
 	Start(process *Process) (err error)
118 118
 
119
-	// Checkpoint checkpoints the running container's state to disk using the criu(8) utility.
120
-	//
121
-	// errors:
122
-	// Systemerror - System error.
123
-	Checkpoint(criuOpts *CriuOpts) error
124
-
125
-	// Restore restores the checkpointed container to a running state using the criu(8) utiity.
126
-	//
127
-	// errors:
128
-	// Systemerror - System error.
129
-	Restore(process *Process, criuOpts *CriuOpts) error
130
-
131 119
 	// Destroys the container after killing all running processes.
132 120
 	//
133 121
 	// Any event registrations are removed before the container is destroyed.
... ...
@@ -137,31 +115,6 @@ type Container interface {
137 137
 	// Systemerror - System error.
138 138
 	Destroy() error
139 139
 
140
-	// If the Container state is RUNNING or PAUSING, sets the Container state to PAUSING and pauses
141
-	// the execution of any user processes. Asynchronously, when the container finished being paused the
142
-	// state is changed to PAUSED.
143
-	// If the Container state is PAUSED, do nothing.
144
-	//
145
-	// errors:
146
-	// ContainerDestroyed - Container no longer exists,
147
-	// Systemerror - System error.
148
-	Pause() error
149
-
150
-	// If the Container state is PAUSED, resumes the execution of any user processes in the
151
-	// Container before setting the Container state to RUNNING.
152
-	// If the Container state is RUNNING, do nothing.
153
-	//
154
-	// errors:
155
-	// ContainerDestroyed - Container no longer exists,
156
-	// Systemerror - System error.
157
-	Resume() error
158
-
159
-	// NotifyOOM returns a read-only channel signaling when the container receives an OOM notification.
160
-	//
161
-	// errors:
162
-	// Systemerror - System error.
163
-	NotifyOOM() (<-chan struct{}, error)
164
-
165 140
 	// Signal sends the provided signal code to the container's initial process.
166 141
 	//
167 142
 	// errors:
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"os"
10 10
 	"os/exec"
11 11
 	"path/filepath"
12
+	"reflect"
12 13
 	"strings"
13 14
 	"sync"
14 15
 	"syscall"
... ...
@@ -32,6 +33,73 @@ type linuxContainer struct {
32 32
 	initProcess   parentProcess
33 33
 	criuPath      string
34 34
 	m             sync.Mutex
35
+	criuVersion   int
36
+}
37
+
38
+// State represents a running container's state
39
+type State struct {
40
+	BaseState
41
+
42
+	// Platform specific fields below here
43
+
44
+	// Path to all the cgroups setup for a container. Key is cgroup subsystem name
45
+	// with the value as the path.
46
+	CgroupPaths map[string]string `json:"cgroup_paths"`
47
+
48
+	// NamespacePaths are filepaths to the container's namespaces. Key is the namespace type
49
+	// with the value as the path.
50
+	NamespacePaths map[configs.NamespaceType]string `json:"namespace_paths"`
51
+
52
+	// Container's standard descriptors (std{in,out,err}), needed for checkpoint and restore
53
+	ExternalDescriptors []string `json:"external_descriptors,omitempty"`
54
+}
55
+
56
+// A libcontainer container object.
57
+//
58
+// Each container is thread-safe within the same process. Since a container can
59
+// be destroyed by a separate process, any function may return that the container
60
+// was not found.
61
+type Container interface {
62
+	BaseContainer
63
+
64
+	// Methods below here are platform specific
65
+
66
+	// Checkpoint checkpoints the running container's state to disk using the criu(8) utility.
67
+	//
68
+	// errors:
69
+	// Systemerror - System error.
70
+	Checkpoint(criuOpts *CriuOpts) error
71
+
72
+	// Restore restores the checkpointed container to a running state using the criu(8) utiity.
73
+	//
74
+	// errors:
75
+	// Systemerror - System error.
76
+	Restore(process *Process, criuOpts *CriuOpts) error
77
+
78
+	// If the Container state is RUNNING or PAUSING, sets the Container state to PAUSING and pauses
79
+	// the execution of any user processes. Asynchronously, when the container finished being paused the
80
+	// state is changed to PAUSED.
81
+	// If the Container state is PAUSED, do nothing.
82
+	//
83
+	// errors:
84
+	// ContainerDestroyed - Container no longer exists,
85
+	// Systemerror - System error.
86
+	Pause() error
87
+
88
+	// If the Container state is PAUSED, resumes the execution of any user processes in the
89
+	// Container before setting the Container state to RUNNING.
90
+	// If the Container state is RUNNING, do nothing.
91
+	//
92
+	// errors:
93
+	// ContainerDestroyed - Container no longer exists,
94
+	// Systemerror - System error.
95
+	Resume() error
96
+
97
+	// NotifyOOM returns a read-only channel signaling when the container receives an OOM notification.
98
+	//
99
+	// errors:
100
+	// Systemerror - System error.
101
+	NotifyOOM() (<-chan struct{}, error)
35 102
 }
36 103
 
37 104
 // ID returns the container's unique ID
... ...
@@ -111,10 +179,25 @@ func (c *linuxContainer) Start(process *Process) error {
111 111
 		}
112 112
 		return newSystemError(err)
113 113
 	}
114
-	process.ops = parent
115 114
 	if doInit {
116 115
 		c.updateState(parent)
117 116
 	}
117
+	if c.config.Hooks != nil {
118
+		s := configs.HookState{
119
+			Version: c.config.Version,
120
+			ID:      c.id,
121
+			Pid:     parent.pid(),
122
+			Root:    c.config.Rootfs,
123
+		}
124
+		for _, hook := range c.config.Hooks.Poststart {
125
+			if err := hook.Run(s); err != nil {
126
+				if err := parent.terminate(); err != nil {
127
+					logrus.Warn(err)
128
+				}
129
+				return newSystemError(err)
130
+			}
131
+		}
132
+	}
118 133
 	return nil
119 134
 }
120 135
 
... ...
@@ -186,6 +269,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
186 186
 		manager:    c.cgroupManager,
187 187
 		config:     c.newInitConfig(p),
188 188
 		container:  c,
189
+		process:    p,
189 190
 	}, nil
190 191
 }
191 192
 
... ...
@@ -204,6 +288,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
204 204
 		childPipe:   childPipe,
205 205
 		parentPipe:  parentPipe,
206 206
 		config:      c.newInitConfig(p),
207
+		process:     p,
207 208
 	}
208 209
 }
209 210
 
... ...
@@ -337,7 +422,9 @@ func (c *linuxContainer) checkCriuVersion(min_version string) error {
337 337
 		}
338 338
 	}
339 339
 
340
-	if x*10000+y*100+z < versionReq {
340
+	c.criuVersion = x*10000 + y*100 + z
341
+
342
+	if c.criuVersion < versionReq {
341 343
 		return fmt.Errorf("CRIU version must be %s or higher", min_version)
342 344
 	}
343 345
 
... ...
@@ -665,6 +752,8 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
665 665
 	defer criuServer.Close()
666 666
 
667 667
 	args := []string{"swrk", "3"}
668
+	logrus.Debugf("Using CRIU %d at: %s", c.criuVersion, c.criuPath)
669
+	logrus.Debugf("Using CRIU with following args: %s", args)
668 670
 	cmd := exec.Command(c.criuPath, args...)
669 671
 	if process != nil {
670 672
 		cmd.Stdin = process.Stdin
... ...
@@ -701,6 +790,18 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
701 701
 		}
702 702
 	}
703 703
 
704
+	logrus.Debugf("Using CRIU in %s mode", req.GetType().String())
705
+	val := reflect.ValueOf(req.GetOpts())
706
+	v := reflect.Indirect(val)
707
+	for i := 0; i < v.NumField(); i++ {
708
+		st := v.Type()
709
+		name := st.Field(i).Name
710
+		if strings.HasPrefix(name, "XXX_") {
711
+			continue
712
+		}
713
+		value := val.MethodByName("Get" + name).Call([]reflect.Value{})
714
+		logrus.Debugf("CRIU option %s with value %v", name, value[0])
715
+	}
704 716
 	data, err := proto.Marshal(req)
705 717
 	if err != nil {
706 718
 		return err
... ...
@@ -899,13 +1000,15 @@ func (c *linuxContainer) currentState() (*State, error) {
899 899
 		return nil, newSystemError(err)
900 900
 	}
901 901
 	state := &State{
902
-		ID:                   c.ID(),
903
-		Config:               *c.config,
904
-		InitProcessPid:       c.initProcess.pid(),
905
-		InitProcessStartTime: startTime,
906
-		CgroupPaths:          c.cgroupManager.GetPaths(),
907
-		NamespacePaths:       make(map[configs.NamespaceType]string),
908
-		ExternalDescriptors:  c.initProcess.externalDescriptors(),
902
+		BaseState: BaseState{
903
+			ID:                   c.ID(),
904
+			Config:               *c.config,
905
+			InitProcessPid:       c.initProcess.pid(),
906
+			InitProcessStartTime: startTime,
907
+		},
908
+		CgroupPaths:         c.cgroupManager.GetPaths(),
909
+		NamespacePaths:      make(map[configs.NamespaceType]string),
910
+		ExternalDescriptors: c.initProcess.externalDescriptors(),
909 911
 	}
910 912
 	for _, ns := range c.config.Namespaces {
911 913
 		state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid())
912 914
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+package libcontainer
1
+
2
+// State represents a running container's state
3
+type State struct {
4
+	BaseState
5
+
6
+	// Platform specific fields below here
7
+}
8
+
9
+// A libcontainer container object.
10
+//
11
+// Each container is thread-safe within the same process. Since a container can
12
+// be destroyed by a separate process, any function may return that the container
13
+// was not found.
14
+type Container interface {
15
+	BaseContainer
16
+
17
+	// Methods below here are platform specific
18
+
19
+}
0 20
deleted file mode 100644
... ...
@@ -1,34 +0,0 @@
1
-package libcontainer
2
-
3
-// cgroup restoring strategy provided by criu
4
-type cg_mode uint32
5
-
6
-const (
7
-	CRIU_CG_MODE_SOFT    cg_mode = 3 + iota // restore cgroup properties if only dir created by criu
8
-	CRIU_CG_MODE_FULL                       // always restore all cgroups and their properties
9
-	CRIU_CG_MODE_STRICT                     // restore all, requiring them to not present in the system
10
-	CRIU_CG_MODE_DEFAULT                    // the same as CRIU_CG_MODE_SOFT
11
-)
12
-
13
-type CriuPageServerInfo struct {
14
-	Address string // IP address of CRIU page server
15
-	Port    int32  // port number of CRIU page server
16
-}
17
-
18
-type VethPairName struct {
19
-	ContainerInterfaceName string
20
-	HostInterfaceName      string
21
-}
22
-
23
-type CriuOpts struct {
24
-	ImagesDirectory         string             // directory for storing image files
25
-	WorkDirectory           string             // directory to cd and write logs/pidfiles/stats to
26
-	LeaveRunning            bool               // leave container in running state after checkpoint
27
-	TcpEstablished          bool               // checkpoint/restore established TCP connections
28
-	ExternalUnixConnections bool               // allow external unix connections
29
-	ShellJob                bool               // allow to dump and restore shell jobs
30
-	FileLocks               bool               // handle file locks, for safety
31
-	PageServer              CriuPageServerInfo // allow to dump to criu page server
32
-	VethPairs               []VethPairName     // pass the veth to criu when restore
33
-	ManageCgroupsMode       cg_mode            // dump or restore cgroup mode
34
-}
35 1
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+// +build linux freebsd
1
+
2
+package libcontainer
3
+
4
+// cgroup restoring strategy provided by criu
5
+type cg_mode uint32
6
+
7
+const (
8
+	CRIU_CG_MODE_SOFT    cg_mode = 3 + iota // restore cgroup properties if only dir created by criu
9
+	CRIU_CG_MODE_FULL                       // always restore all cgroups and their properties
10
+	CRIU_CG_MODE_STRICT                     // restore all, requiring them to not present in the system
11
+	CRIU_CG_MODE_DEFAULT                    // the same as CRIU_CG_MODE_SOFT
12
+)
13
+
14
+type CriuPageServerInfo struct {
15
+	Address string // IP address of CRIU page server
16
+	Port    int32  // port number of CRIU page server
17
+}
18
+
19
+type VethPairName struct {
20
+	ContainerInterfaceName string
21
+	HostInterfaceName      string
22
+}
23
+
24
+type CriuOpts struct {
25
+	ImagesDirectory         string             // directory for storing image files
26
+	WorkDirectory           string             // directory to cd and write logs/pidfiles/stats to
27
+	LeaveRunning            bool               // leave container in running state after checkpoint
28
+	TcpEstablished          bool               // checkpoint/restore established TCP connections
29
+	ExternalUnixConnections bool               // allow external unix connections
30
+	ShellJob                bool               // allow to dump and restore shell jobs
31
+	FileLocks               bool               // handle file locks, for safety
32
+	PageServer              CriuPageServerInfo // allow to dump to criu page server
33
+	VethPairs               []VethPairName     // pass the veth to criu when restore
34
+	ManageCgroupsMode       cg_mode            // dump or restore cgroup mode
35
+}
0 36
new file mode 100644
... ...
@@ -0,0 +1,6 @@
0
+package libcontainer
1
+
2
+// TODO Windows: This can ultimately be entirely factored out as criu is
3
+// a Unix concept not relevant on Windows.
4
+type CriuOpts struct {
5
+}
0 6
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+// +build windows
1
+
2
+package devices
0 3
deleted file mode 100644
... ...
@@ -1,16 +0,0 @@
1
-package devices
2
-
3
-import (
4
-	"github.com/opencontainers/runc/libcontainer/configs"
5
-)
6
-
7
-// TODO Windows. This can be factored out further - Devices are not supported
8
-// by Windows Containers.
9
-
10
-func DeviceFromPath(path, permissions string) (*configs.Device, error) {
11
-	return nil, nil
12
-}
13
-
14
-func HostDevices() ([]*configs.Device, error) {
15
-	return nil, nil
16
-}
... ...
@@ -159,7 +159,7 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
159 159
 	}
160 160
 	containerRoot := filepath.Join(l.Root, id)
161 161
 	if _, err := os.Stat(containerRoot); err == nil {
162
-		return nil, newGenericError(fmt.Errorf("Container with id exists: %v", id), IdInUse)
162
+		return nil, newGenericError(fmt.Errorf("container with id exists: %v", id), IdInUse)
163 163
 	} else if !os.IsNotExist(err) {
164 164
 		return nil, newGenericError(err, SystemError)
165 165
 	}
... ...
@@ -210,9 +210,10 @@ func (l *LinuxFactory) Type() string {
210 210
 // StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
211 211
 // This is a low level implementation detail of the reexec and should not be consumed externally
212 212
 func (l *LinuxFactory) StartInitialization() (err error) {
213
-	pipefd, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_INITPIPE"))
213
+	fdStr := os.Getenv("_LIBCONTAINER_INITPIPE")
214
+	pipefd, err := strconv.Atoi(fdStr)
214 215
 	if err != nil {
215
-		return err
216
+		return fmt.Errorf("error converting env var _LIBCONTAINER_INITPIPE(%q) to an int: %s", fdStr, err)
216 217
 	}
217 218
 	var (
218 219
 		pipe = os.NewFile(uintptr(pipefd), "pipe")
... ...
@@ -260,10 +261,10 @@ func (l *LinuxFactory) loadState(root string) (*State, error) {
260 260
 
261 261
 func (l *LinuxFactory) validateID(id string) error {
262 262
 	if !idRegex.MatchString(id) {
263
-		return newGenericError(fmt.Errorf("Invalid id format: %v", id), InvalidIdFormat)
263
+		return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
264 264
 	}
265 265
 	if len(id) > maxIdLen {
266
-		return newGenericError(fmt.Errorf("Invalid id format: %v", id), InvalidIdFormat)
266
+		return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
267 267
 	}
268 268
 	return nil
269 269
 }
... ...
@@ -47,6 +47,7 @@ type setnsProcess struct {
47 47
 	cgroupPaths map[string]string
48 48
 	config      *initConfig
49 49
 	fds         []string
50
+	process     *Process
50 51
 }
51 52
 
52 53
 func (p *setnsProcess) startTime() (string, error) {
... ...
@@ -87,7 +88,6 @@ func (p *setnsProcess) start() (err error) {
87 87
 		p.wait()
88 88
 		return newSystemError(ierr)
89 89
 	}
90
-
91 90
 	return nil
92 91
 }
93 92
 
... ...
@@ -115,13 +115,12 @@ func (p *setnsProcess) execSetns() error {
115 115
 		p.cmd.Wait()
116 116
 		return newSystemError(err)
117 117
 	}
118
-
119 118
 	process, err := os.FindProcess(pid.Pid)
120 119
 	if err != nil {
121 120
 		return err
122 121
 	}
123
-
124 122
 	p.cmd.Process = process
123
+	p.process.ops = p
125 124
 	return nil
126 125
 }
127 126
 
... ...
@@ -165,6 +164,7 @@ type initProcess struct {
165 165
 	manager    cgroups.Manager
166 166
 	container  *linuxContainer
167 167
 	fds        []string
168
+	process    *Process
168 169
 }
169 170
 
170 171
 func (p *initProcess) pid() int {
... ...
@@ -178,8 +178,10 @@ func (p *initProcess) externalDescriptors() []string {
178 178
 func (p *initProcess) start() (err error) {
179 179
 	defer p.parentPipe.Close()
180 180
 	err = p.cmd.Start()
181
+	p.process.ops = p
181 182
 	p.childPipe.Close()
182 183
 	if err != nil {
184
+		p.process.ops = nil
183 185
 		return newSystemError(err)
184 186
 	}
185 187
 	// Save the standard descriptor names before the container process
... ...
@@ -29,7 +29,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole) (err error) {
29 29
 		return newSystemError(err)
30 30
 	}
31 31
 
32
-	setupDev := len(config.Devices) == 0
32
+	setupDev := len(config.Devices) != 0
33 33
 	for _, m := range config.Mounts {
34 34
 		for _, precmd := range m.PremountCmds {
35 35
 			if err := mountCmd(precmd); err != nil {
... ...
@@ -46,7 +46,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole) (err error) {
46 46
 			}
47 47
 		}
48 48
 	}
49
-	if !setupDev {
49
+	if setupDev {
50 50
 		if err := createDevices(config); err != nil {
51 51
 			return newSystemError(err)
52 52
 		}
... ...
@@ -68,7 +68,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole) (err error) {
68 68
 	if err != nil {
69 69
 		return newSystemError(err)
70 70
 	}
71
-	if !setupDev {
71
+	if setupDev {
72 72
 		if err := reOpenDevNull(); err != nil {
73 73
 			return newSystemError(err)
74 74
 		}
... ...
@@ -290,8 +290,7 @@ func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) {
290 290
 	return binds, nil
291 291
 }
292 292
 
293
-// checkMountDestination checks to ensure that the mount destination is not over the
294
-// top of /proc or /sys.
293
+// checkMountDestination checks to ensure that the mount destination is not over the top of /proc.
295 294
 // dest is required to be an abs path and have any symlinks resolved before calling this function.
296 295
 func checkMountDestination(rootfs, dest string) error {
297 296
 	if filepath.Clean(rootfs) == filepath.Clean(dest) {
... ...
@@ -379,6 +378,17 @@ func createDevices(config *configs.Config) error {
379 379
 	return nil
380 380
 }
381 381
 
382
+func bindMountDeviceNode(dest string, node *configs.Device) error {
383
+	f, err := os.Create(dest)
384
+	if err != nil && !os.IsExist(err) {
385
+		return err
386
+	}
387
+	if f != nil {
388
+		f.Close()
389
+	}
390
+	return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
391
+}
392
+
382 393
 // Creates the device node in the rootfs of the container.
383 394
 func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
384 395
 	dest := filepath.Join(rootfs, node.Path)
... ...
@@ -387,18 +397,13 @@ func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
387 387
 	}
388 388
 
389 389
 	if bind {
390
-		f, err := os.Create(dest)
391
-		if err != nil && !os.IsExist(err) {
392
-			return err
393
-		}
394
-		if f != nil {
395
-			f.Close()
396
-		}
397
-		return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
390
+		return bindMountDeviceNode(dest, node)
398 391
 	}
399 392
 	if err := mknodDevice(dest, node); err != nil {
400 393
 		if os.IsExist(err) {
401 394
 			return nil
395
+		} else if os.IsPermission(err) {
396
+			return bindMountDeviceNode(dest, node)
402 397
 		}
403 398
 		return err
404 399
 	}
... ...
@@ -634,7 +639,6 @@ func remount(m *configs.Mount, rootfs string) error {
634 634
 	if !strings.HasPrefix(dest, rootfs) {
635 635
 		dest = filepath.Join(rootfs, dest)
636 636
 	}
637
-
638 637
 	if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil {
639 638
 		return err
640 639
 	}
... ...
@@ -6,29 +6,47 @@ import (
6 6
 	"github.com/opencontainers/runc/libcontainer/configs"
7 7
 )
8 8
 
9
+var operators = map[string]configs.Operator{
10
+	"SCMP_CMP_NE":        configs.NotEqualTo,
11
+	"SCMP_CMP_LT":        configs.LessThan,
12
+	"SCMP_CMP_LE":        configs.LessThanOrEqualTo,
13
+	"SCMP_CMP_EQ":        configs.EqualTo,
14
+	"SCMP_CMP_GE":        configs.GreaterThanOrEqualTo,
15
+	"SCMP_CMP_GT":        configs.GreaterThan,
16
+	"SCMP_CMP_MASKED_EQ": configs.MaskEqualTo,
17
+}
18
+
19
+var actions = map[string]configs.Action{
20
+	"SCMP_ACT_KILL":  configs.Kill,
21
+	"SCMP_ACT_ERRNO": configs.Errno,
22
+	"SCMP_ACT_TRAP":  configs.Trap,
23
+	"SCMP_ACT_ALLOW": configs.Allow,
24
+	"SCMP_ACT_TRACE": configs.Trace,
25
+}
26
+
27
+var archs = map[string]string{
28
+	"SCMP_ARCH_X86":         "x86",
29
+	"SCMP_ARCH_X86_64":      "amd64",
30
+	"SCMP_ARCH_X32":         "x32",
31
+	"SCMP_ARCH_ARM":         "arm",
32
+	"SCMP_ARCH_AARCH64":     "arm64",
33
+	"SCMP_ARCH_MIPS":        "mips",
34
+	"SCMP_ARCH_MIPS64":      "mips64",
35
+	"SCMP_ARCH_MIPS64N32":   "mips64n32",
36
+	"SCMP_ARCH_MIPSEL":      "mipsel",
37
+	"SCMP_ARCH_MIPSEL64":    "mipsel64",
38
+	"SCMP_ARCH_MIPSEL64N32": "mipsel64n32",
39
+}
40
+
9 41
 // ConvertStringToOperator converts a string into a Seccomp comparison operator.
10 42
 // Comparison operators use the names they are assigned by Libseccomp's header.
11 43
 // Attempting to convert a string that is not a valid operator results in an
12 44
 // error.
13 45
 func ConvertStringToOperator(in string) (configs.Operator, error) {
14
-	switch in {
15
-	case "SCMP_CMP_NE":
16
-		return configs.NotEqualTo, nil
17
-	case "SCMP_CMP_LT":
18
-		return configs.LessThan, nil
19
-	case "SCMP_CMP_LE":
20
-		return configs.LessThanOrEqualTo, nil
21
-	case "SCMP_CMP_EQ":
22
-		return configs.EqualTo, nil
23
-	case "SCMP_CMP_GE":
24
-		return configs.GreaterThan, nil
25
-	case "SCMP_CMP_GT":
26
-		return configs.GreaterThanOrEqualTo, nil
27
-	case "SCMP_CMP_MASKED_EQ":
28
-		return configs.MaskEqualTo, nil
29
-	default:
30
-		return 0, fmt.Errorf("string %s is not a valid operator for seccomp", in)
46
+	if op, ok := operators[in]; ok == true {
47
+		return op, nil
31 48
 	}
49
+	return 0, fmt.Errorf("string %s is not a valid operator for seccomp", in)
32 50
 }
33 51
 
34 52
 // ConvertStringToAction converts a string into a Seccomp rule match action.
... ...
@@ -38,16 +56,16 @@ func ConvertStringToOperator(in string) (configs.Operator, error) {
38 38
 // Attempting to convert a string that is not a valid action results in an
39 39
 // error.
40 40
 func ConvertStringToAction(in string) (configs.Action, error) {
41
-	switch in {
42
-	case "SCMP_ACT_KILL":
43
-		return configs.Kill, nil
44
-	case "SCMP_ACT_ERRNO":
45
-		return configs.Errno, nil
46
-	case "SCMP_ACT_TRAP":
47
-		return configs.Trap, nil
48
-	case "SCMP_ACT_ALLOW":
49
-		return configs.Allow, nil
50
-	default:
51
-		return 0, fmt.Errorf("string %s is not a valid action for seccomp", in)
41
+	if act, ok := actions[in]; ok == true {
42
+		return act, nil
43
+	}
44
+	return 0, fmt.Errorf("string %s is not a valid action for seccomp", in)
45
+}
46
+
47
+// ConvertStringToArch converts a string into a Seccomp comparison arch.
48
+func ConvertStringToArch(in string) (string, error) {
49
+	if arch, ok := archs[in]; ok == true {
50
+		return arch, nil
52 51
 	}
52
+	return "", fmt.Errorf("string %s is not a valid arch for seccomp", in)
53 53
 }
... ...
@@ -15,6 +15,7 @@ var (
15 15
 	actAllow = libseccomp.ActAllow
16 16
 	actTrap  = libseccomp.ActTrap
17 17
 	actKill  = libseccomp.ActKill
18
+	actTrace = libseccomp.ActTrace.SetReturnCode(int16(syscall.EPERM))
18 19
 	actErrno = libseccomp.ActErrno.SetReturnCode(int16(syscall.EPERM))
19 20
 )
20 21
 
... ...
@@ -83,6 +84,8 @@ func getAction(act configs.Action) (libseccomp.ScmpAction, error) {
83 83
 		return actTrap, nil
84 84
 	case configs.Allow:
85 85
 		return actAllow, nil
86
+	case configs.Trace:
87
+		return actTrace, nil
86 88
 	default:
87 89
 		return libseccomp.ActInvalid, fmt.Errorf("invalid action, cannot use in rule")
88 90
 	}
... ...
@@ -36,7 +36,7 @@ func ResolveRootfs(uncleanRootfs string) (string, error) {
36 36
 }
37 37
 
38 38
 // ExitStatus returns the correct exit status for a process based on if it
39
-// was signaled or existed cleanly.
39
+// was signaled or exited cleanly.
40 40
 func ExitStatus(status syscall.WaitStatus) int {
41 41
 	if status.Signaled() {
42 42
 		return exitSignalOffset + int(status.Signal())
... ...
@@ -86,6 +86,10 @@ func getVfsCap(path string, dest *vfscapData) (err error) {
86 86
 	}
87 87
 	r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0)
88 88
 	if e1 != 0 {
89
+		if e1 == syscall.ENODATA {
90
+			dest.version = 2
91
+			return
92
+		}
89 93
 		err = e1
90 94
 	}
91 95
 	switch dest.magic & vfsCapVerMask {
... ...
@@ -128,8 +132,6 @@ func setVfsCap(path string, data *vfscapData) (err error) {
128 128
 		data.magic = vfsCapVer2
129 129
 		if data.effective[0] != 0 || data.effective[1] != 0 {
130 130
 			data.magic |= vfsCapFlageffective
131
-			data.data[0].permitted |= data.effective[0]
132
-			data.data[1].permitted |= data.effective[1]
133 131
 		}
134 132
 		size = vfscapDataSizeV2
135 133
 	} else {