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>
| ... | ... |
@@ -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 |
-} |
| 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) {
|
| ... | ... |
@@ -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 |
+} |
| ... | ... |
@@ -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 |
+} |
| ... | ... |
@@ -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 |
+} |
| ... | ... |
@@ -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 |
} |
| ... | ... |
@@ -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 | 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 {
|