update runc godep

This commit is contained in:
David Ashpole 2017-08-25 13:42:26 -07:00
parent 995cd0a212
commit 8ba18456d5
377 changed files with 152993 additions and 2666 deletions

148
Godeps/Godeps.json generated
View File

@ -1,16 +1,11 @@
{ {
"ImportPath": "github.com/google/cadvisor", "ImportPath": "github.com/google/cadvisor",
"GoVersion": "go1.6", "GoVersion": "go1.8",
"GodepVersion": "v79", "GodepVersion": "v79",
"Packages": [ "Packages": [
"./..." "./..."
], ],
"Deps": [ "Deps": [
{
"ImportPath": "bitbucket.org/ww/goautoneg",
"Comment": "null-5",
"Rev": "'75cd24fc2f2c2a2088577d12123ddee5f54e0675'"
},
{ {
"ImportPath": "cloud.google.com/go/compute/metadata", "ImportPath": "cloud.google.com/go/compute/metadata",
"Comment": "v0.1.0-115-g3b1ae45", "Comment": "v0.1.0-115-g3b1ae45",
@ -35,11 +30,6 @@
"Comment": "v1.8.0", "Comment": "v1.8.0",
"Rev": "4ba9bba6adb6697bcec3841e1ecdfecf5227c3b9" "Rev": "4ba9bba6adb6697bcec3841e1ecdfecf5227c3b9"
}, },
{
"ImportPath": "github.com/Sirupsen/logrus",
"Comment": "v0.7.3-2-g26709e2",
"Rev": "26709e2714106fb8ad40b773b711ebce25b78914"
},
{ {
"ImportPath": "github.com/abbot/go-http-auth", "ImportPath": "github.com/abbot/go-http-auth",
"Rev": "c0ef4539dfab4d21c8ef20ba2924f9fc6f186d35" "Rev": "c0ef4539dfab4d21c8ef20ba2924f9fc6f186d35"
@ -185,12 +175,12 @@
}, },
{ {
"ImportPath": "github.com/docker/distribution/digest", "ImportPath": "github.com/docker/distribution/digest",
"Comment": "v2.4.0-rc.1-30-gc6f63e2", "Comment": "v2.4.0-rc.1-30-gc6f63e29",
"Rev": "c6f63e298ebb1dd0a2ce5ada0c0d39e248132634" "Rev": "c6f63e298ebb1dd0a2ce5ada0c0d39e248132634"
}, },
{ {
"ImportPath": "github.com/docker/distribution/reference", "ImportPath": "github.com/docker/distribution/reference",
"Comment": "v2.4.0-rc.1-30-gc6f63e2", "Comment": "v2.4.0-rc.1-30-gc6f63e29",
"Rev": "c6f63e298ebb1dd0a2ce5ada0c0d39e248132634" "Rev": "c6f63e298ebb1dd0a2ce5ada0c0d39e248132634"
}, },
{ {
@ -280,18 +270,18 @@
}, },
{ {
"ImportPath": "github.com/docker/go-connections/nat", "ImportPath": "github.com/docker/go-connections/nat",
"Comment": "v0.2.0-2-gf549a93", "Comment": "v0.2.1-30-g3ede32e",
"Rev": "f549a9393d05688dff0992ef3efd8bbe6c628aeb" "Rev": "3ede32e2033de7505e6500d6c868c2b9ed9f169d"
}, },
{ {
"ImportPath": "github.com/docker/go-connections/sockets", "ImportPath": "github.com/docker/go-connections/sockets",
"Comment": "v0.2.0-2-gf549a93", "Comment": "v0.2.1-30-g3ede32e",
"Rev": "f549a9393d05688dff0992ef3efd8bbe6c628aeb" "Rev": "3ede32e2033de7505e6500d6c868c2b9ed9f169d"
}, },
{ {
"ImportPath": "github.com/docker/go-connections/tlsconfig", "ImportPath": "github.com/docker/go-connections/tlsconfig",
"Comment": "v0.2.0-2-gf549a93", "Comment": "v0.2.1-30-g3ede32e",
"Rev": "f549a9393d05688dff0992ef3efd8bbe6c628aeb" "Rev": "3ede32e2033de7505e6500d6c868c2b9ed9f169d"
}, },
{ {
"ImportPath": "github.com/docker/go-units", "ImportPath": "github.com/docker/go-units",
@ -332,7 +322,7 @@
}, },
{ {
"ImportPath": "github.com/golang/protobuf/proto", "ImportPath": "github.com/golang/protobuf/proto",
"Rev": "b982704f8bb716bb608144408cff30e15fbde841" "Rev": "ab9f9a6dab164b7d1246e0e688b0ab7b94d8553e"
}, },
{ {
"ImportPath": "github.com/golang/snappy", "ImportPath": "github.com/golang/snappy",
@ -386,98 +376,103 @@
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer", "ImportPath": "github.com/opencontainers/runc/libcontainer",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/apparmor", "ImportPath": "github.com/opencontainers/runc/libcontainer/apparmor",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups", "ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/fs", "ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/fs",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/rootless", "ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/rootless",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/systemd", "ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/systemd",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs", "ImportPath": "github.com/opencontainers/runc/libcontainer/configs",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs/validate", "ImportPath": "github.com/opencontainers/runc/libcontainer/configs/validate",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/criurpc", "ImportPath": "github.com/opencontainers/runc/libcontainer/criurpc",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/keys", "ImportPath": "github.com/opencontainers/runc/libcontainer/keys",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/label",
"Comment": "v1.0.0-rc3-15-g4d6225ae",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/seccomp", "ImportPath": "github.com/opencontainers/runc/libcontainer/seccomp",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/selinux",
"Comment": "v1.0.0-rc3-15-g4d6225ae",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/stacktrace", "ImportPath": "github.com/opencontainers/runc/libcontainer/stacktrace",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/system", "ImportPath": "github.com/opencontainers/runc/libcontainer/system",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/user", "ImportPath": "github.com/opencontainers/runc/libcontainer/user",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/utils", "ImportPath": "github.com/opencontainers/runc/libcontainer/utils",
"Comment": "v1.0.0-rc3-15-g4d6225ae", "Comment": "v1.0.0-rc4-50-g4d6e6720",
"Rev": "4d6225aec25991f62fb37e5fec0ec5853c660f9f" "Rev": "4d6e6720a7c885c37b4cb083c0d372dda3425120"
}, },
{ {
"ImportPath": "github.com/opencontainers/runtime-spec/specs-go", "ImportPath": "github.com/opencontainers/runtime-spec/specs-go",
"Comment": "v1.0.0-rc5-67-gf227620", "Comment": "v1.0.0-rc5-67-gf227620",
"Rev": "f2276206b32ad0c2478bfc6440ceb7d51d815cf8" "Rev": "f2276206b32ad0c2478bfc6440ceb7d51d815cf8"
}, },
{
"ImportPath": "github.com/opencontainers/selinux/go-selinux",
"Comment": "v1.0.0-rc1-5-g4a2974b",
"Rev": "4a2974bf1ee960774ffd517717f1f45325af0206"
},
{
"ImportPath": "github.com/opencontainers/selinux/go-selinux/label",
"Comment": "v1.0.0-rc1-5-g4a2974b",
"Rev": "4a2974bf1ee960774ffd517717f1f45325af0206"
},
{ {
"ImportPath": "github.com/pborman/uuid", "ImportPath": "github.com/pborman/uuid",
"Rev": "cccd189d45f7ac3368a0d127efb7f4d08ae0b655" "Rev": "cccd189d45f7ac3368a0d127efb7f4d08ae0b655"
}, },
{
"ImportPath": "github.com/pkg/errors",
"Comment": "v0.8.0-5-gc605e28",
"Rev": "c605e284fe17294bda444b34710735b29d1a9d90"
},
{ {
"ImportPath": "github.com/prometheus/client_golang/prometheus", "ImportPath": "github.com/prometheus/client_golang/prometheus",
"Comment": "v0.8.0-62-g08fd2e1", "Comment": "v0.8.0-62-g08fd2e1",
@ -497,6 +492,10 @@
"ImportPath": "github.com/prometheus/common/expfmt", "ImportPath": "github.com/prometheus/common/expfmt",
"Rev": "49fee292b27bfff7f354ee0f64e1bc4850462edf" "Rev": "49fee292b27bfff7f354ee0f64e1bc4850462edf"
}, },
{
"ImportPath": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg",
"Rev": "49fee292b27bfff7f354ee0f64e1bc4850462edf"
},
{ {
"ImportPath": "github.com/prometheus/common/model", "ImportPath": "github.com/prometheus/common/model",
"Rev": "49fee292b27bfff7f354ee0f64e1bc4850462edf" "Rev": "49fee292b27bfff7f354ee0f64e1bc4850462edf"
@ -505,10 +504,19 @@
"ImportPath": "github.com/prometheus/procfs", "ImportPath": "github.com/prometheus/procfs",
"Rev": "1e2146578273cef808354faa16a1922e0b5d6b2f" "Rev": "1e2146578273cef808354faa16a1922e0b5d6b2f"
}, },
{
"ImportPath": "github.com/prometheus/procfs/xfs",
"Rev": "1e2146578273cef808354faa16a1922e0b5d6b2f"
},
{ {
"ImportPath": "github.com/seccomp/libseccomp-golang", "ImportPath": "github.com/seccomp/libseccomp-golang",
"Rev": "1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1" "Rev": "1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1"
}, },
{
"ImportPath": "github.com/sirupsen/logrus",
"Comment": "v1.0.3-11-g89742ae",
"Rev": "89742aefa4b206dcf400792f3bd35b542998eb3b"
},
{ {
"ImportPath": "github.com/stretchr/objx", "ImportPath": "github.com/stretchr/objx",
"Rev": "cbeaeb16a013161a98496fad62933b1d21786672" "Rev": "cbeaeb16a013161a98496fad62933b1d21786672"
@ -537,6 +545,10 @@
"ImportPath": "github.com/vishvananda/netlink/nl", "ImportPath": "github.com/vishvananda/netlink/nl",
"Rev": "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270" "Rev": "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270"
}, },
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "eb71ad9bd329b5ac0fd0148dd99bd62e8be8e035"
},
{ {
"ImportPath": "golang.org/x/exp/inotify", "ImportPath": "golang.org/x/exp/inotify",
"Rev": "292a51b8d262487dab23a588950e8052d63d9113" "Rev": "292a51b8d262487dab23a588950e8052d63d9113"
@ -585,6 +597,14 @@
"ImportPath": "golang.org/x/oauth2/jwt", "ImportPath": "golang.org/x/oauth2/jwt",
"Rev": "ca8a464d23d55afd32571475db223e065ffd8a52" "Rev": "ca8a464d23d55afd32571475db223e065ffd8a52"
}, },
{
"ImportPath": "golang.org/x/sys/unix",
"Rev": "22e23b74325bf60e4cc1ce7bdc157eda0d8b5c13"
},
{
"ImportPath": "golang.org/x/sys/windows",
"Rev": "22e23b74325bf60e4cc1ce7bdc157eda0d8b5c13"
},
{ {
"ImportPath": "google.golang.org/api/bigquery/v2", "ImportPath": "google.golang.org/api/bigquery/v2",
"Rev": "0c2979aeaa5b573e60d3ddffe5ce8dca8df309bd" "Rev": "0c2979aeaa5b573e60d3ddffe5ce8dca8df309bd"
@ -638,14 +658,6 @@
"ImportPath": "gopkg.in/olivere/elastic.v2/uritemplates", "ImportPath": "gopkg.in/olivere/elastic.v2/uritemplates",
"Comment": "v2.0.12", "Comment": "v2.0.12",
"Rev": "3cfe88295d20b6299bd935131fc0fd17316405ad" "Rev": "3cfe88295d20b6299bd935131fc0fd17316405ad"
},
{
"ImportPath": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg",
"Rev": "49fee292b27bfff7f354ee0f64e1bc4850462edf"
},
{
"ImportPath": "github.com/prometheus/procfs/xfs",
"Rev": "1e2146578273cef808354faa16a1922e0b5d6b2f"
} }
] ]
} }

View File

@ -41,16 +41,16 @@ func TestRefresh(t *testing.T) {
{ {
name: "check reservation fails", name: "check reservation fails",
dmsetupCommands: []fake.DmsetupCommand{ dmsetupCommands: []fake.DmsetupCommand{
{"status", "", fmt.Errorf("not gonna work")}, {Name: "status", Result: "", Err: fmt.Errorf("not gonna work")},
}, },
expectedError: true, expectedError: true,
}, },
{ {
name: "no existing reservation - ok with minimum # of fields", name: "no existing reservation - ok with minimum # of fields",
dmsetupCommands: []fake.DmsetupCommand{ dmsetupCommands: []fake.DmsetupCommand{
{"status", "0 75497472 thin-pool 65 327/524288 14092/589824 -", nil}, // status check {Name: "status", Result: "0 75497472 thin-pool 65 327/524288 14092/589824 -", Err: nil}, // status check
{"message", "", nil}, // make reservation {Name: "message", Result: "", Err: nil}, // make reservation
{"message", "", nil}, // release reservation {Name: "message", Result: "", Err: nil}, // release reservation
}, },
thinLsOutput: usage, thinLsOutput: usage,
expectedError: false, expectedError: false,
@ -60,9 +60,9 @@ func TestRefresh(t *testing.T) {
{ {
name: "no existing reservation - ok", name: "no existing reservation - ok",
dmsetupCommands: []fake.DmsetupCommand{ dmsetupCommands: []fake.DmsetupCommand{
{"status", "0 75497472 thin-pool 65 327/524288 14092/589824 - rw no_discard_passdown error_if_no_space - ", nil}, // status check {Name: "status", Result: "0 75497472 thin-pool 65 327/524288 14092/589824 - rw no_discard_passdown error_if_no_space - ", Err: nil}, // status check
{"message", "", nil}, // make reservation {Name: "message", Result: "", Err: nil}, // make reservation
{"message", "", nil}, // release reservation {Name: "message", Result: "", Err: nil}, // release reservation
}, },
thinLsOutput: usage, thinLsOutput: usage,
expectedError: false, expectedError: false,
@ -73,13 +73,13 @@ func TestRefresh(t *testing.T) {
name: "existing reservation - ok", name: "existing reservation - ok",
dmsetupCommands: []fake.DmsetupCommand{ dmsetupCommands: []fake.DmsetupCommand{
// status check // status check
{"status", "0 75497472 thin-pool 65 327/524288 14092/589824 39 rw no_discard_passdown error_if_no_space - ", nil}, {Name: "status", Result: "0 75497472 thin-pool 65 327/524288 14092/589824 39 rw no_discard_passdown error_if_no_space - ", Err: nil},
// release reservation // release reservation
{"message", "", nil}, {Name: "message", Result: "", Err: nil},
// make reservation // make reservation
{"message", "", nil}, {Name: "message", Result: "", Err: nil},
// release reservation // release reservation
{"message", "", nil}, {Name: "message", Result: "", Err: nil},
}, },
thinLsOutput: usage, thinLsOutput: usage,
expectedError: false, expectedError: false,
@ -90,9 +90,9 @@ func TestRefresh(t *testing.T) {
name: "failure releasing existing reservation", name: "failure releasing existing reservation",
dmsetupCommands: []fake.DmsetupCommand{ dmsetupCommands: []fake.DmsetupCommand{
// status check // status check
{"status", "0 75497472 thin-pool 65 327/524288 14092/589824 39 rw no_discard_passdown error_if_no_space - ", nil}, {Name: "status", Result: "0 75497472 thin-pool 65 327/524288 14092/589824 39 rw no_discard_passdown error_if_no_space - ", Err: nil},
// release reservation // release reservation
{"message", "", fmt.Errorf("not gonna work")}, {Name: "message", Result: "", Err: fmt.Errorf("not gonna work")},
}, },
expectedError: true, expectedError: true,
}, },
@ -100,11 +100,11 @@ func TestRefresh(t *testing.T) {
name: "failure making reservation", name: "failure making reservation",
dmsetupCommands: []fake.DmsetupCommand{ dmsetupCommands: []fake.DmsetupCommand{
// status check // status check
{"status", "0 75497472 thin-pool 65 327/524288 14092/589824 39 rw no_discard_passdown error_if_no_space - ", nil}, {Name: "status", Result: "0 75497472 thin-pool 65 327/524288 14092/589824 39 rw no_discard_passdown error_if_no_space - ", Err: nil},
// release reservation // release reservation
{"message", "", nil}, {Name: "message", Result: "", Err: nil},
// make reservation // make reservation
{"message", "", fmt.Errorf("not gonna work")}, {Name: "message", Result: "", Err: fmt.Errorf("not gonna work")},
}, },
expectedError: true, expectedError: true,
}, },
@ -112,13 +112,13 @@ func TestRefresh(t *testing.T) {
name: "failure running thin_ls", name: "failure running thin_ls",
dmsetupCommands: []fake.DmsetupCommand{ dmsetupCommands: []fake.DmsetupCommand{
// status check // status check
{"status", "0 75497472 thin-pool 65 327/524288 14092/589824 39 rw no_discard_passdown error_if_no_space - ", nil}, {Name: "status", Result: "0 75497472 thin-pool 65 327/524288 14092/589824 39 rw no_discard_passdown error_if_no_space - ", Err: nil},
// release reservation // release reservation
{"message", "", nil}, {Name: "message", Result: "", Err: nil},
// make reservation // make reservation
{"message", "", nil}, {Name: "message", Result: "", Err: nil},
// release reservation // release reservation
{"message", "", nil}, {Name: "message", Result: "", Err: nil},
}, },
thinLsErr: fmt.Errorf("not gonna work"), thinLsErr: fmt.Errorf("not gonna work"),
expectedError: true, expectedError: true,

View File

@ -1,13 +0,0 @@
include $(GOROOT)/src/Make.inc
TARG=bitbucket.org/ww/goautoneg
GOFILES=autoneg.go
include $(GOROOT)/src/Make.pkg
format:
gofmt -w *.go
docs:
gomake clean
godoc ${TARG} > README.txt

View File

@ -1,67 +0,0 @@
PACKAGE
package goautoneg
import "bitbucket.org/ww/goautoneg"
HTTP Content-Type Autonegotiation.
The functions in this package implement the behaviour specified in
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
Copyright (c) 2011, Open Knowledge Foundation Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
Neither the name of the Open Knowledge Foundation Ltd. nor the
names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
FUNCTIONS
func Negotiate(header string, alternatives []string) (content_type string)
Negotiate the most appropriate content_type given the accept header
and a list of alternatives.
func ParseAccept(header string) (accept []Accept)
Parse an Accept Header string returning a sorted list
of clauses
TYPES
type Accept struct {
Type, SubType string
Q float32
Params map[string]string
}
Structure to represent a clause in an HTTP Accept Header
SUBDIRECTORIES
.hg

View File

@ -1,162 +0,0 @@
/*
HTTP Content-Type Autonegotiation.
The functions in this package implement the behaviour specified in
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
Copyright (c) 2011, Open Knowledge Foundation Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
Neither the name of the Open Knowledge Foundation Ltd. nor the
names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package goautoneg
import (
"sort"
"strconv"
"strings"
)
// Structure to represent a clause in an HTTP Accept Header
type Accept struct {
Type, SubType string
Q float64
Params map[string]string
}
// For internal use, so that we can use the sort interface
type accept_slice []Accept
func (accept accept_slice) Len() int {
slice := []Accept(accept)
return len(slice)
}
func (accept accept_slice) Less(i, j int) bool {
slice := []Accept(accept)
ai, aj := slice[i], slice[j]
if ai.Q > aj.Q {
return true
}
if ai.Type != "*" && aj.Type == "*" {
return true
}
if ai.SubType != "*" && aj.SubType == "*" {
return true
}
return false
}
func (accept accept_slice) Swap(i, j int) {
slice := []Accept(accept)
slice[i], slice[j] = slice[j], slice[i]
}
// Parse an Accept Header string returning a sorted list
// of clauses
func ParseAccept(header string) (accept []Accept) {
parts := strings.Split(header, ",")
accept = make([]Accept, 0, len(parts))
for _, part := range parts {
part := strings.Trim(part, " ")
a := Accept{}
a.Params = make(map[string]string)
a.Q = 1.0
mrp := strings.Split(part, ";")
media_range := mrp[0]
sp := strings.Split(media_range, "/")
a.Type = strings.Trim(sp[0], " ")
switch {
case len(sp) == 1 && a.Type == "*":
a.SubType = "*"
case len(sp) == 2:
a.SubType = strings.Trim(sp[1], " ")
default:
continue
}
if len(mrp) == 1 {
accept = append(accept, a)
continue
}
for _, param := range mrp[1:] {
sp := strings.SplitN(param, "=", 2)
if len(sp) != 2 {
continue
}
token := strings.Trim(sp[0], " ")
if token == "q" {
a.Q, _ = strconv.ParseFloat(sp[1], 32)
} else {
a.Params[token] = strings.Trim(sp[1], " ")
}
}
accept = append(accept, a)
}
slice := accept_slice(accept)
sort.Sort(slice)
return
}
// Negotiate the most appropriate content_type given the accept header
// and a list of alternatives.
func Negotiate(header string, alternatives []string) (content_type string) {
asp := make([][]string, 0, len(alternatives))
for _, ctype := range alternatives {
asp = append(asp, strings.SplitN(ctype, "/", 2))
}
for _, clause := range ParseAccept(header) {
for i, ctsp := range asp {
if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
content_type = alternatives[i]
return
}
if clause.Type == ctsp[0] && clause.SubType == "*" {
content_type = alternatives[i]
return
}
if clause.Type == "*" && clause.SubType == "*" {
content_type = alternatives[i]
return
}
}
}
return
}

15
vendor/cloud.google.com/go/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,15 @@
# This is the official list of cloud authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Filippo Valsorda <hi@filippo.io>
Google Inc.
Ingo Oeser <nightlyone@googlemail.com>
Palm Stone Games, Inc.
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Tyler Treat <ttreat31@gmail.com>

34
vendor/cloud.google.com/go/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,34 @@
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
# Name <email address>
# Keep the list alphabetically sorted.
Andreas Litt <andreas.litt@gmail.com>
Andrew Gerrand <adg@golang.org>
Brad Fitzpatrick <bradfitz@golang.org>
Burcu Dogan <jbd@google.com>
Dave Day <djd@golang.org>
David Sansome <me@davidsansome.com>
David Symonds <dsymonds@golang.org>
Filippo Valsorda <hi@filippo.io>
Glenn Lewis <gmlewis@google.com>
Ingo Oeser <nightlyone@googlemail.com>
Johan Euphrosine <proppy@google.com>
Jonathan Amsterdam <jba@google.com>
Luna Duclos <luna.duclos@palmstonegames.com>
Michael McGreevy <mcgreevy@golang.org>
Omar Jarjur <ojarjur@google.com>
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Sarah Adams <shadams@google.com>
Toby Burress <kurin@google.com>
Tuo Shan <shantuo@google.com>
Tyler Treat <ttreat31@gmail.com>

View File

@ -1,8 +0,0 @@
language: go
go:
- 1.2
- 1.3
- 1.4
- tip
install:
- go get -t ./...

View File

@ -1,7 +0,0 @@
# 0.7.3
formatter/\*: allow configuration of timestamp layout
# 0.7.2
formatter/text: Add configuration option for time format (#158)

View File

@ -1,349 +0,0 @@
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus)&nbsp;[![godoc reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][godoc]
Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
many large deployments. The core API is unlikely to change much but please
version control your Logrus to make sure you aren't fetching latest `master` on
every build.**
Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):
![Colored](http://i.imgur.com/PY7qMwd.png)
With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
or Splunk:
```json
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
{"level":"warning","msg":"The group's number increased tremendously!",
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
```
With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
attached, the output is compatible with the
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
```text
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
exit status 1
```
#### Example
The simplest way to use Logrus is simply the package-level exported logger:
```go
package main
import (
log "github.com/Sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
```
Note that it's completely api-compatible with the stdlib logger, so you can
replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
and you'll now have the flexibility of Logrus. You can customize it all you
want:
```go
package main
import (
"os"
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus/hooks/airbrake"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
// Use the Airbrake hook to report errors that have Error severity or above to
// an exception tracker. You can create custom hooks, see the Hooks section.
log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
// Output to stderr instead of stdout, could also be a file.
log.SetOutput(os.Stderr)
// Only log the warning severity or above.
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log.WithFields(log.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}
```
For more advanced usage such as logging to multiple locations from the same
application, you can also create an instance of the `logrus` Logger:
```go
package main
import (
"github.com/Sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
var log = logrus.New()
func main() {
// The API for setting attributes is a little different than the package level
// exported logger. See Godoc.
log.Out = os.Stderr
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
```
#### Fields
Logrus encourages careful, structured logging though logging fields instead of
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
to send event %s to topic %s with key %d")`, you should log the much more
discoverable:
```go
log.WithFields(log.Fields{
"event": event,
"topic": topic,
"key": key,
}).Fatal("Failed to send event")
```
We've found this API forces you to think about logging in a way that produces
much more useful logging messages. We've been in countless situations where just
a single added field to a log statement that was already there would've saved us
hours. The `WithFields` call is optional.
In general, with Logrus using any of the `printf`-family functions should be
seen as a hint you should add a field, however, you can still use the
`printf`-family functions with Logrus.
#### Hooks
You can add hooks for logging levels. For example to send errors to an exception
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
multiple places simultaneously, e.g. syslog.
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
`init`:
```go
import (
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus/hooks/airbrake"
"github.com/Sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init() {
log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
log.Error("Unable to connect to local syslog daemon")
} else {
log.AddHook(hook)
}
}
```
| Hook | Description |
| ----- | ----------- |
| [Airbrake](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) | Send errors to an exception tracking service compatible with the Airbrake API. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
| [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail hosted logging service via UDP. |
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
| [BugSnag](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
| [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) |
#### Level logging
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
```go
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
log.Fatal("Bye.")
// Calls panic() after logging
log.Panic("I'm bailing.")
```
You can set the logging level on a `Logger`, then it will only log entries with
that severity or anything above it:
```go
// Will log anything that is info or above (warn, error, fatal, panic). Default.
log.SetLevel(log.InfoLevel)
```
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
environment if your application has that.
#### Entries
Besides the fields added with `WithField` or `WithFields` some fields are
automatically added to all logging events:
1. `time`. The timestamp when the entry was created.
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
the `AddFields` call. E.g. `Failed to send event.`
3. `level`. The logging level. E.g. `info`.
#### Environments
Logrus has no notion of environment.
If you wish for hooks and formatters to only be used in specific environments,
you should handle that yourself. For example, if your application has a global
variable `Environment`, which is a string representation of the environment you
could do:
```go
import (
log "github.com/Sirupsen/logrus"
)
init() {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
log.SetFormatter(logrus.JSONFormatter)
} else {
// The TextFormatter is default, you don't actually have to do this.
log.SetFormatter(logrus.TextFormatter)
}
}
```
This configuration is how `logrus` was intended to be used, but JSON in
production is mostly only useful if you do log aggregation with tools like
Splunk or Logstash.
#### Formatters
The built-in logging formatters are:
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
without colors.
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
field to `true`. To force no colored output even if there is a TTY set the
`DisableColors` field to `true`
* `logrus.JSONFormatter`. Logs fields as JSON.
* `logrus_logstash.LogstashFormatter`. Logs fields as Logstash Events (http://logstash.net).
```go
logrus.SetFormatter(&logrus_logstash.LogstashFormatter{Type: “application_name"})
```
Third party logging formatters:
* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
`Fields` type (`map[string]interface{}`) with all your fields as well as the
default ones (see Entries section above):
```go
type MyJSONFormatter struct {
}
log.SetFormatter(new(MyJSONFormatter))
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
// Note this doesn't include Time, Level and Message which are available on
// the Entry. Consult `godoc` on information about those fields or read the
// source of the official loggers.
serialized, err := json.Marshal(entry.Data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}
```
#### Logger as an `io.Writer`
Logrus can be transormed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
```go
w := logger.Writer()
defer w.Close()
srv := http.Server{
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog: log.New(w, "", 0),
}
```
Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is `info`.
#### Rotation
Log rotation is not provided with Logrus. Log rotation should be done by an
external program (like `logrotate(8)`) that can compress and delete old log
entries. It should not be a feature of the application-level logger.
[godoc]: https://godoc.org/github.com/Sirupsen/logrus

View File

@ -1,40 +0,0 @@
package logrus
import (
"encoding/json"
"fmt"
)
type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps.
TimestampFormat string
}
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data := make(Fields, len(entry.Data)+3)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
// https://github.com/Sirupsen/logrus/issues/137
data[k] = v.Error()
default:
data[k] = v
}
}
prefixFieldClashes(data)
if f.TimestampFormat == "" {
f.TimestampFormat = DefaultTimestampFormat
}
data["time"] = entry.Time.Format(f.TimestampFormat)
data["msg"] = entry.Message
data["level"] = entry.Level.String()
serialized, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}

View File

@ -1,203 +0,0 @@
package logrus
import (
"io"
"os"
"sync"
)
type Logger struct {
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
// file, or leave it default which is `os.Stdout`. You can also set this to
// something more adventorous, such as logging to Kafka.
Out io.Writer
// Hooks for the logger instance. These allow firing events based on logging
// levels and log entries. For example, to send errors to an error tracking
// service, log to StatsD or dump the core on fatal errors.
Hooks levelHooks
// All log entries pass through the formatter before logged to Out. The
// included formatters are `TextFormatter` and `JSONFormatter` for which
// TextFormatter is the default. In development (when a TTY is attached) it
// logs with colors, but to a file it wouldn't. You can easily implement your
// own that implements the `Formatter` interface, see the `README` or included
// formatters for examples.
Formatter Formatter
// The logging level the logger should log at. This is typically (and defaults
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
// logged. `logrus.Debug` is useful in
Level Level
// Used to sync writing to the log.
mu sync.Mutex
}
// Creates a new logger. Configuration should be set by changing `Formatter`,
// `Out` and `Hooks` directly on the default logger instance. You can also just
// instantiate your own:
//
// var log = &Logger{
// Out: os.Stderr,
// Formatter: new(JSONFormatter),
// Hooks: make(levelHooks),
// Level: logrus.DebugLevel,
// }
//
// It's recommended to make this a global instance called `log`.
func New() *Logger {
return &Logger{
Out: os.Stdout,
Formatter: new(TextFormatter),
Hooks: make(levelHooks),
Level: InfoLevel,
}
}
// Adds a field to the log entry, note that you it doesn't log until you call
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
// Ff you want multiple fields, use `WithFields`.
func (logger *Logger) WithField(key string, value interface{}) *Entry {
return NewEntry(logger).WithField(key, value)
}
// Adds a struct of fields to the log entry. All it does is call `WithField` for
// each `Field`.
func (logger *Logger) WithFields(fields Fields) *Entry {
return NewEntry(logger).WithFields(fields)
}
func (logger *Logger) Debugf(format string, args ...interface{}) {
if logger.Level >= DebugLevel {
NewEntry(logger).Debugf(format, args...)
}
}
func (logger *Logger) Infof(format string, args ...interface{}) {
if logger.Level >= InfoLevel {
NewEntry(logger).Infof(format, args...)
}
}
func (logger *Logger) Printf(format string, args ...interface{}) {
NewEntry(logger).Printf(format, args...)
}
func (logger *Logger) Warnf(format string, args ...interface{}) {
if logger.Level >= WarnLevel {
NewEntry(logger).Warnf(format, args...)
}
}
func (logger *Logger) Warningf(format string, args ...interface{}) {
if logger.Level >= WarnLevel {
NewEntry(logger).Warnf(format, args...)
}
}
func (logger *Logger) Errorf(format string, args ...interface{}) {
if logger.Level >= ErrorLevel {
NewEntry(logger).Errorf(format, args...)
}
}
func (logger *Logger) Fatalf(format string, args ...interface{}) {
if logger.Level >= FatalLevel {
NewEntry(logger).Fatalf(format, args...)
}
}
func (logger *Logger) Panicf(format string, args ...interface{}) {
if logger.Level >= PanicLevel {
NewEntry(logger).Panicf(format, args...)
}
}
func (logger *Logger) Debug(args ...interface{}) {
if logger.Level >= DebugLevel {
NewEntry(logger).Debug(args...)
}
}
func (logger *Logger) Info(args ...interface{}) {
if logger.Level >= InfoLevel {
NewEntry(logger).Info(args...)
}
}
func (logger *Logger) Print(args ...interface{}) {
NewEntry(logger).Info(args...)
}
func (logger *Logger) Warn(args ...interface{}) {
if logger.Level >= WarnLevel {
NewEntry(logger).Warn(args...)
}
}
func (logger *Logger) Warning(args ...interface{}) {
if logger.Level >= WarnLevel {
NewEntry(logger).Warn(args...)
}
}
func (logger *Logger) Error(args ...interface{}) {
if logger.Level >= ErrorLevel {
NewEntry(logger).Error(args...)
}
}
func (logger *Logger) Fatal(args ...interface{}) {
if logger.Level >= FatalLevel {
NewEntry(logger).Fatal(args...)
}
}
func (logger *Logger) Panic(args ...interface{}) {
if logger.Level >= PanicLevel {
NewEntry(logger).Panic(args...)
}
}
func (logger *Logger) Debugln(args ...interface{}) {
if logger.Level >= DebugLevel {
NewEntry(logger).Debugln(args...)
}
}
func (logger *Logger) Infoln(args ...interface{}) {
if logger.Level >= InfoLevel {
NewEntry(logger).Infoln(args...)
}
}
func (logger *Logger) Println(args ...interface{}) {
NewEntry(logger).Println(args...)
}
func (logger *Logger) Warnln(args ...interface{}) {
if logger.Level >= WarnLevel {
NewEntry(logger).Warnln(args...)
}
}
func (logger *Logger) Warningln(args ...interface{}) {
if logger.Level >= WarnLevel {
NewEntry(logger).Warnln(args...)
}
}
func (logger *Logger) Errorln(args ...interface{}) {
if logger.Level >= ErrorLevel {
NewEntry(logger).Errorln(args...)
}
}
func (logger *Logger) Fatalln(args ...interface{}) {
if logger.Level >= FatalLevel {
NewEntry(logger).Fatalln(args...)
}
}
func (logger *Logger) Panicln(args ...interface{}) {
if logger.Level >= PanicLevel {
NewEntry(logger).Panicln(args...)
}
}

View File

@ -1,20 +0,0 @@
/*
Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
*/
package logrus
import (
"syscall"
)
const ioctlReadTermios = syscall.TIOCGETA
type Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]uint8
Ispeed uint32
Ospeed uint32
}

View File

@ -1,21 +0,0 @@
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux darwin freebsd openbsd
package logrus
import (
"syscall"
"unsafe"
)
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal() bool {
fd := syscall.Stdout
var termios Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View File

@ -1,7 +0,0 @@
package logrus
import "syscall"
const ioctlReadTermios = syscall.TIOCGETA
type Termios syscall.Termios

View File

@ -1,27 +0,0 @@
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package logrus
import (
"syscall"
"unsafe"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
)
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal() bool {
fd := syscall.Stdout
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}

View File

@ -1,149 +0,0 @@
package logrus
import (
"bytes"
"fmt"
"sort"
"strings"
"time"
)
const (
nocolor = 0
red = 31
green = 32
yellow = 33
blue = 34
gray = 37
)
var (
baseTimestamp time.Time
isTerminal bool
)
func init() {
baseTimestamp = time.Now()
isTerminal = IsTerminal()
}
func miniTS() int {
return int(time.Since(baseTimestamp) / time.Second)
}
type TextFormatter struct {
// Set to true to bypass checking for a TTY before outputting colors.
ForceColors bool
// Force disabling colors.
DisableColors bool
// Disable timestamp logging. useful when output is redirected to logging
// system that already adds timestamps.
DisableTimestamp bool
// Enable logging the full timestamp when a TTY is attached instead of just
// the time passed since beginning of execution.
FullTimestamp bool
// TimestampFormat to use for display when a full timestamp is printed
TimestampFormat string
// The fields are sorted by default for a consistent output. For applications
// that log extremely frequently and don't use the JSON formatter this may not
// be desired.
DisableSorting bool
}
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
var keys []string = make([]string, 0, len(entry.Data))
for k := range entry.Data {
keys = append(keys, k)
}
if !f.DisableSorting {
sort.Strings(keys)
}
b := &bytes.Buffer{}
prefixFieldClashes(entry.Data)
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
if f.TimestampFormat == "" {
f.TimestampFormat = DefaultTimestampFormat
}
if isColored {
f.printColored(b, entry, keys)
} else {
if !f.DisableTimestamp {
f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat))
}
f.appendKeyValue(b, "level", entry.Level.String())
f.appendKeyValue(b, "msg", entry.Message)
for _, key := range keys {
f.appendKeyValue(b, key, entry.Data[key])
}
}
b.WriteByte('\n')
return b.Bytes(), nil
}
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) {
var levelColor int
switch entry.Level {
case DebugLevel:
levelColor = gray
case WarnLevel:
levelColor = yellow
case ErrorLevel, FatalLevel, PanicLevel:
levelColor = red
default:
levelColor = blue
}
levelText := strings.ToUpper(entry.Level.String())[0:4]
if !f.FullTimestamp {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
} else {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(f.TimestampFormat), entry.Message)
}
for _, k := range keys {
v := entry.Data[k]
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
}
}
func needsQuoting(text string) bool {
for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
ch == '-' || ch == '.') {
return false
}
}
return true
}
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
switch value.(type) {
case string:
if needsQuoting(value.(string)) {
fmt.Fprintf(b, "%v=%s ", key, value)
} else {
fmt.Fprintf(b, "%v=%q ", key, value)
}
case error:
if needsQuoting(value.(error).Error()) {
fmt.Fprintf(b, "%v=%s ", key, value)
} else {
fmt.Fprintf(b, "%v=%q ", key, value)
}
default:
fmt.Fprintf(b, "%v=%v ", key, value)
}
}

View File

@ -1,31 +0,0 @@
package logrus
import (
"bufio"
"io"
"runtime"
)
func (logger *Logger) Writer() *io.PipeWriter {
reader, writer := io.Pipe()
go logger.writerScanner(reader)
runtime.SetFinalizer(writer, writerFinalizer)
return writer
}
func (logger *Logger) writerScanner(reader *io.PipeReader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
logger.Print(scanner.Text())
}
if err := scanner.Err(); err != nil {
logger.Errorf("Error while reading from Writer: %s", err)
}
reader.Close()
}
func writerFinalizer(writer *io.PipeWriter) {
writer.Close()
}

128
vendor/github.com/docker/distribution/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,128 @@
Aaron Lehmann <aaron.lehmann@docker.com>
Aaron Vinson <avinson.public@gmail.com>
Adam Enger <adamenger@gmail.com>
Adrian Mouat <adrian.mouat@gmail.com>
Ahmet Alp Balkan <ahmetalpbalkan@gmail.com>
Alex Chan <alex.chan@metaswitch.com>
Alex Elman <aelman@indeed.com>
amitshukla <ashukla73@hotmail.com>
Amy Lindburg <amy.lindburg@docker.com>
Andrew Meredith <andymeredith@gmail.com>
Andrew T Nguyen <andrew.nguyen@docker.com>
Andrey Kostov <kostov.andrey@gmail.com>
Andy Goldstein <agoldste@redhat.com>
Anton Tiurin <noxiouz@yandex.ru>
Antonio Mercado <amercado@thinknode.com>
Antonio Murdaca <runcom@redhat.com>
Arnaud Porterie <arnaud.porterie@docker.com>
Arthur Baars <arthur@semmle.com>
Asuka Suzuki <hello@tanksuzuki.com>
Avi Miller <avi.miller@oracle.com>
Ayose Cazorla <ayosec@gmail.com>
BadZen <dave.trombley@gmail.com>
Ben Firshman <ben@firshman.co.uk>
bin liu <liubin0329@gmail.com>
Brian Bland <brian.bland@docker.com>
burnettk <burnettk@gmail.com>
Carson A <ca@carsonoid.net>
Chris Dillon <squarism@gmail.com>
Daisuke Fujita <dtanshi45@gmail.com>
Darren Shepherd <darren@rancher.com>
Dave Trombley <dave.trombley@gmail.com>
Dave Tucker <dt@docker.com>
David Lawrence <david.lawrence@docker.com>
David Verhasselt <david@crowdway.com>
David Xia <dxia@spotify.com>
davidli <wenquan.li@hp.com>
Dejan Golja <dejan@golja.org>
Derek McGowan <derek@mcgstyle.net>
Diogo Mónica <diogo.monica@gmail.com>
DJ Enriquez <dj.enriquez@infospace.com>
Donald Huang <don.hcd@gmail.com>
Doug Davis <dug@us.ibm.com>
Eric Yang <windfarer@gmail.com>
farmerworking <farmerworking@gmail.com>
Felix Yan <felixonmars@archlinux.org>
Florentin Raud <florentin.raud@gmail.com>
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
gabriell nascimento <gabriell@bluesoft.com.br>
harche <p.harshal@gmail.com>
Henri Gomez <henri.gomez@gmail.com>
Hu Keping <hukeping@huawei.com>
Hua Wang <wanghua.humble@gmail.com>
HuKeping <hukeping@huawei.com>
Ian Babrou <ibobrik@gmail.com>
igayoso <igayoso@gmail.com>
Jack Griffin <jackpg14@gmail.com>
Jason Freidman <jason.freidman@gmail.com>
Jeff Nickoloff <jeff@allingeek.com>
Jessie Frazelle <jessie@docker.com>
Jianqing Wang <tsing@jianqing.org>
John Starks <jostarks@microsoft.com>
Jon Poler <jonathan.poler@apcera.com>
Jonathan Boulle <jonathanboulle@gmail.com>
Jordan Liggitt <jliggitt@redhat.com>
Josh Hawn <josh.hawn@docker.com>
Julien Fernandez <julien.fernandez@gmail.com>
Keerthan Mala <kmala@engineyard.com>
Kelsey Hightower <kelsey.hightower@gmail.com>
Kenneth Lim <kennethlimcp@gmail.com>
Kenny Leung <kleung@google.com>
Li Yi <denverdino@gmail.com>
Liu Hua <sdu.liu@huawei.com>
liuchang0812 <liuchang0812@gmail.com>
Louis Kottmann <louis.kottmann@gmail.com>
Luke Carpenter <x@rubynerd.net>
Mary Anthony <mary@docker.com>
Matt Bentley <mbentley@mbentley.net>
Matt Duch <matt@learnmetrics.com>
Matt Moore <mattmoor@google.com>
Matt Robenolt <matt@ydekproductions.com>
Michael Prokop <mika@grml.org>
Michal Minar <miminar@redhat.com>
Miquel Sabaté <msabate@suse.com>
Morgan Bauer <mbauer@us.ibm.com>
moxiegirl <mary@docker.com>
Nathan Sullivan <nathan@nightsys.net>
nevermosby <robolwq@qq.com>
Nghia Tran <tcnghia@gmail.com>
Nuutti Kotivuori <nuutti.kotivuori@poplatek.fi>
Oilbeater <liumengxinfly@gmail.com>
Olivier Gambier <olivier@docker.com>
Olivier Jacques <olivier.jacques@hp.com>
Omer Cohen <git@omer.io>
Patrick Devine <patrick.devine@docker.com>
Philip Misiowiec <philip@atlashealth.com>
Richard Scothern <richard.scothern@docker.com>
Rodolfo Carvalho <rhcarvalho@gmail.com>
Rusty Conover <rusty@luckydinosaur.com>
Sean Boran <Boran@users.noreply.github.com>
Sebastiaan van Stijn <github@gone.nl>
Sharif Nassar <sharif@mrwacky.com>
Shawn Falkner-Horine <dreadpirateshawn@gmail.com>
Shreyas Karnik <karnik.shreyas@gmail.com>
Simon Thulbourn <simon+github@thulbourn.com>
Spencer Rinehart <anubis@overthemonkey.com>
Stefan Weil <sw@weilnetz.de>
Stephen J Day <stephen.day@docker.com>
Sungho Moon <sungho.moon@navercorp.com>
Sven Dowideit <SvenDowideit@home.org.au>
Sylvain Baubeau <sbaubeau@redhat.com>
Ted Reed <ted.reed@gmail.com>
tgic <farmer1992@gmail.com>
Thomas Sjögren <konstruktoid@users.noreply.github.com>
Tianon Gravi <admwiggin@gmail.com>
Tibor Vass <teabee89@gmail.com>
Tonis Tiigi <tonistiigi@gmail.com>
Trevor Pounds <trevor.pounds@gmail.com>
Troels Thomsen <troels@thomsen.io>
Vincent Batts <vbatts@redhat.com>
Vincent Demeester <vincent@sbr.pm>
Vincent Giersch <vincent.giersch@ovh.net>
W. Trevor King <wking@tremily.us>
weiyuan.yl <weiyuan.yl@alibaba-inc.com>
xg.song <xg.song@venusource.com>
xiekeyang <xiekeyang@huawei.com>
Yann ROBERT <yann.robert@anantaplex.fr>
yuzou <zouyu7@huawei.com>
姜继忠 <jizhong.jiangjz@alibaba-inc.com>

1460
vendor/github.com/docker/docker/AUTHORS generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -85,14 +85,10 @@ func (p Port) Port() string {
// Int returns the port number of a Port as an int // Int returns the port number of a Port as an int
func (p Port) Int() int { func (p Port) Int() int {
portStr := p.Port() portStr := p.Port()
if len(portStr) == 0 {
return 0
}
// We don't need to check for an error because we're going to // We don't need to check for an error because we're going to
// assume that any error would have been found, and reported, in NewPort() // assume that any error would have been found, and reported, in NewPort()
port, _ := strconv.ParseUint(portStr, 10, 16) port, _ := ParsePort(portStr)
return int(port) return port
} }
// Range returns the start/end port numbers of a Port range as ints // Range returns the start/end port numbers of a Port range as ints
@ -132,92 +128,115 @@ func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
exposedPorts = make(map[Port]struct{}, len(ports)) exposedPorts = make(map[Port]struct{}, len(ports))
bindings = make(map[Port][]PortBinding) bindings = make(map[Port][]PortBinding)
) )
for _, rawPort := range ports { for _, rawPort := range ports {
proto := "tcp" portMappings, err := ParsePortSpec(rawPort)
if i := strings.LastIndex(rawPort, "/"); i != -1 {
proto = rawPort[i+1:]
rawPort = rawPort[:i]
}
if !strings.Contains(rawPort, ":") {
rawPort = fmt.Sprintf("::%s", rawPort)
} else if len(strings.Split(rawPort, ":")) == 2 {
rawPort = fmt.Sprintf(":%s", rawPort)
}
parts, err := PartParser(portSpecTemplate, rawPort)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
var ( for _, portMapping := range portMappings {
containerPort = parts["containerPort"] port := portMapping.Port
rawIP = parts["ip"]
hostPort = parts["hostPort"]
)
if rawIP != "" && net.ParseIP(rawIP) == nil {
return nil, nil, fmt.Errorf("Invalid ip address: %s", rawIP)
}
if containerPort == "" {
return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
}
startPort, endPort, err := ParsePortRange(containerPort)
if err != nil {
return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
}
var startHostPort, endHostPort uint64 = 0, 0
if len(hostPort) > 0 {
startHostPort, endHostPort, err = ParsePortRange(hostPort)
if err != nil {
return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
}
}
if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
// Allow host port range iff containerPort is not a range.
// In this case, use the host port range as the dynamic
// host port range to allocate into.
if endPort != startPort {
return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
}
}
if !validateProto(strings.ToLower(proto)) {
return nil, nil, fmt.Errorf("Invalid proto: %s", proto)
}
for i := uint64(0); i <= (endPort - startPort); i++ {
containerPort = strconv.FormatUint(startPort+i, 10)
if len(hostPort) > 0 {
hostPort = strconv.FormatUint(startHostPort+i, 10)
}
// Set hostPort to a range only if there is a single container port
// and a dynamic host port.
if startPort == endPort && startHostPort != endHostPort {
hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10))
}
port, err := NewPort(strings.ToLower(proto), containerPort)
if err != nil {
return nil, nil, err
}
if _, exists := exposedPorts[port]; !exists { if _, exists := exposedPorts[port]; !exists {
exposedPorts[port] = struct{}{} exposedPorts[port] = struct{}{}
} }
binding := PortBinding{
HostIP: rawIP,
HostPort: hostPort,
}
bslice, exists := bindings[port] bslice, exists := bindings[port]
if !exists { if !exists {
bslice = []PortBinding{} bslice = []PortBinding{}
} }
bindings[port] = append(bslice, binding) bindings[port] = append(bslice, portMapping.Binding)
} }
} }
return exposedPorts, bindings, nil return exposedPorts, bindings, nil
} }
// PortMapping is a data object mapping a Port to a PortBinding
type PortMapping struct {
Port Port
Binding PortBinding
}
func splitParts(rawport string) (string, string, string) {
parts := strings.Split(rawport, ":")
n := len(parts)
containerport := parts[n-1]
switch n {
case 1:
return "", "", containerport
case 2:
return "", parts[0], containerport
case 3:
return parts[0], parts[1], containerport
default:
return strings.Join(parts[:n-2], ":"), parts[n-2], containerport
}
}
// ParsePortSpec parses a port specification string into a slice of PortMappings
func ParsePortSpec(rawPort string) ([]PortMapping, error) {
var proto string
rawIP, hostPort, containerPort := splitParts(rawPort)
proto, containerPort = SplitProtoPort(containerPort)
// Strip [] from IPV6 addresses
ip, _, err := net.SplitHostPort(rawIP + ":")
if err != nil {
return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err)
}
if ip != "" && net.ParseIP(ip) == nil {
return nil, fmt.Errorf("Invalid ip address: %s", ip)
}
if containerPort == "" {
return nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
}
startPort, endPort, err := ParsePortRange(containerPort)
if err != nil {
return nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
}
var startHostPort, endHostPort uint64 = 0, 0
if len(hostPort) > 0 {
startHostPort, endHostPort, err = ParsePortRange(hostPort)
if err != nil {
return nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
}
}
if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
// Allow host port range iff containerPort is not a range.
// In this case, use the host port range as the dynamic
// host port range to allocate into.
if endPort != startPort {
return nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
}
}
if !validateProto(strings.ToLower(proto)) {
return nil, fmt.Errorf("Invalid proto: %s", proto)
}
ports := []PortMapping{}
for i := uint64(0); i <= (endPort - startPort); i++ {
containerPort = strconv.FormatUint(startPort+i, 10)
if len(hostPort) > 0 {
hostPort = strconv.FormatUint(startHostPort+i, 10)
}
// Set hostPort to a range only if there is a single container port
// and a dynamic host port.
if startPort == endPort && startHostPort != endHostPort {
hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10))
}
port, err := NewPort(strings.ToLower(proto), containerPort)
if err != nil {
return nil, err
}
binding := PortBinding{
HostIP: ip,
HostPort: hostPort,
}
ports = append(ports, PortMapping{Port: port, Binding: binding})
}
return ports, nil
}

View File

@ -8,6 +8,7 @@ import (
// PartParser parses and validates the specified string (data) using the specified template // PartParser parses and validates the specified string (data) using the specified template
// e.g. ip:public:private -> 192.168.0.1:80:8000 // e.g. ip:public:private -> 192.168.0.1:80:8000
// DEPRECATED: do not use, this function may be removed in a future version
func PartParser(template, data string) (map[string]string, error) { func PartParser(template, data string) (map[string]string, error) {
// ip:public:private // ip:public:private
var ( var (

View File

@ -79,11 +79,3 @@ func (a dummyAddr) Network() string {
func (a dummyAddr) String() string { func (a dummyAddr) String() string {
return string(a) return string(a)
} }
// timeoutError is used when there is a timeout with a connection
// this implements the net.Error interface
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }

View File

@ -2,6 +2,7 @@
package sockets package sockets
import ( import (
"errors"
"net" "net"
"net/http" "net/http"
"time" "time"
@ -10,6 +11,9 @@ import (
// Why 32? See https://github.com/docker/docker/pull/8035. // Why 32? See https://github.com/docker/docker/pull/8035.
const defaultTimeout = 32 * time.Second const defaultTimeout = 32 * time.Second
// ErrProtocolNotAvailable is returned when a given transport protocol is not provided by the operating system.
var ErrProtocolNotAvailable = errors.New("protocol not available")
// ConfigureTransport configures the specified Transport according to the // ConfigureTransport configures the specified Transport according to the
// specified proto and addr. // specified proto and addr.
// If the proto is unix (using a unix socket to communicate) or npipe the // If the proto is unix (using a unix socket to communicate) or npipe the
@ -17,17 +21,9 @@ const defaultTimeout = 32 * time.Second
func ConfigureTransport(tr *http.Transport, proto, addr string) error { func ConfigureTransport(tr *http.Transport, proto, addr string) error {
switch proto { switch proto {
case "unix": case "unix":
// No need for compression in local communications. return configureUnixTransport(tr, proto, addr)
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, defaultTimeout)
}
case "npipe": case "npipe":
// No need for compression in local communications. return configureNpipeTransport(tr, proto, addr)
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return DialPipe(addr, defaultTimeout)
}
default: default:
tr.Proxy = http.ProxyFromEnvironment tr.Proxy = http.ProxyFromEnvironment
dialer, err := DialerFromEnvironment(&net.Dialer{ dialer, err := DialerFromEnvironment(&net.Dialer{

View File

@ -3,11 +3,31 @@
package sockets package sockets
import ( import (
"fmt"
"net" "net"
"net/http"
"syscall" "syscall"
"time" "time"
) )
const maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
func configureUnixTransport(tr *http.Transport, proto, addr string) error {
if len(addr) > maxUnixSocketPathSize {
return fmt.Errorf("Unix socket path %q is too long", addr)
}
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, defaultTimeout)
}
return nil
}
func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
return ErrProtocolNotAvailable
}
// DialPipe connects to a Windows named pipe. // DialPipe connects to a Windows named pipe.
// This is not supported on other OSes. // This is not supported on other OSes.
func DialPipe(_ string, _ time.Duration) (net.Conn, error) { func DialPipe(_ string, _ time.Duration) (net.Conn, error) {

View File

@ -2,11 +2,25 @@ package sockets
import ( import (
"net" "net"
"net/http"
"time" "time"
"github.com/Microsoft/go-winio" "github.com/Microsoft/go-winio"
) )
func configureUnixTransport(tr *http.Transport, proto, addr string) error {
return ErrProtocolNotAvailable
}
func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return DialPipe(addr, defaultTimeout)
}
return nil
}
// DialPipe connects to a Windows named pipe. // DialPipe connects to a Windows named pipe.
func DialPipe(addr string, timeout time.Duration) (net.Conn, error) { func DialPipe(addr string, timeout time.Duration) (net.Conn, error) {
return winio.DialPipe(addr, &timeout) return winio.DialPipe(addr, &timeout)

View File

@ -7,7 +7,7 @@ import (
) )
// NewTCPSocket creates a TCP socket listener with the specified address and // NewTCPSocket creates a TCP socket listener with the specified address and
// and the specified tls configuration. If TLSConfig is set, will encapsulate the // the specified tls configuration. If TLSConfig is set, will encapsulate the
// TCP listener inside a TLS one. // TCP listener inside a TLS one.
func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) { func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) {
l, err := net.Listen("tcp", addr) l, err := net.Listen("tcp", addr)

View File

@ -1,30 +1,26 @@
// +build linux freebsd solaris // +build !windows
package sockets package sockets
import ( import (
"fmt"
"net" "net"
"os" "os"
"strconv"
"syscall" "syscall"
"github.com/Sirupsen/logrus"
"github.com/opencontainers/runc/libcontainer/user"
) )
// NewUnixSocket creates a unix socket with the specified path and group. // NewUnixSocket creates a unix socket with the specified path and group.
func NewUnixSocket(path, group string) (net.Listener, error) { func NewUnixSocket(path string, gid int) (net.Listener, error) {
if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) { if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
return nil, err return nil, err
} }
mask := syscall.Umask(0777) mask := syscall.Umask(0777)
defer syscall.Umask(mask) defer syscall.Umask(mask)
l, err := net.Listen("unix", path) l, err := net.Listen("unix", path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := setSocketGroup(path, group); err != nil { if err := os.Chown(path, 0, gid); err != nil {
l.Close() l.Close()
return nil, err return nil, err
} }
@ -34,47 +30,3 @@ func NewUnixSocket(path, group string) (net.Listener, error) {
} }
return l, nil return l, nil
} }
func setSocketGroup(path, group string) error {
if group == "" {
return nil
}
if err := changeGroup(path, group); err != nil {
if group != "docker" {
return err
}
logrus.Debugf("Warning: could not change group %s to docker: %v", path, err)
}
return nil
}
func changeGroup(path string, nameOrGid string) error {
gid, err := lookupGidByName(nameOrGid)
if err != nil {
return err
}
logrus.Debugf("%s group found. gid: %d", nameOrGid, gid)
return os.Chown(path, 0, gid)
}
func lookupGidByName(nameOrGid string) (int, error) {
groupFile, err := user.GetGroupPath()
if err != nil {
return -1, err
}
groups, err := user.ParseGroupFileFilter(groupFile, func(g user.Group) bool {
return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
})
if err != nil {
return -1, err
}
if groups != nil && len(groups) > 0 {
return groups[0].Gid, nil
}
gid, err := strconv.Atoi(nameOrGid)
if err == nil {
logrus.Warnf("Could not find GID %d", gid)
return gid, nil
}
return -1, fmt.Errorf("Group %s not found", nameOrGid)
}

View File

@ -0,0 +1,18 @@
// +build go1.7
package tlsconfig
import (
"crypto/x509"
"runtime"
)
// SystemCertPool returns a copy of the system cert pool,
// returns an error if failed to load or empty pool on windows.
func SystemCertPool() (*x509.CertPool, error) {
certpool, err := x509.SystemCertPool()
if err != nil && runtime.GOOS == "windows" {
return x509.NewCertPool(), nil
}
return certpool, err
}

View File

@ -0,0 +1,14 @@
// +build !go1.7
package tlsconfig
import (
"crypto/x509"
)
// SystemCertPool returns an new empty cert pool,
// accessing system cert pool is supported in go 1.7
func SystemCertPool() (*x509.CertPool, error) {
return x509.NewCertPool(), nil
}

View File

@ -8,11 +8,12 @@ package tlsconfig
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/pem"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/Sirupsen/logrus" "github.com/pkg/errors"
) )
// Options represents the information needed to create client and server TLS configurations. // Options represents the information needed to create client and server TLS configurations.
@ -29,6 +30,14 @@ type Options struct {
InsecureSkipVerify bool InsecureSkipVerify bool
// server-only option // server-only option
ClientAuth tls.ClientAuthType ClientAuth tls.ClientAuthType
// If ExclusiveRootPools is set, then if a CA file is provided, the root pool used for TLS
// creds will include exclusively the roots in that CA file. If no CA file is provided,
// the system pool will be used.
ExclusiveRootPools bool
MinVersion uint16
// If Passphrase is set, it will be used to decrypt a TLS private key
// if the key is encrypted
Passphrase string
} }
// Extra (server-side) accepted CBC cipher suites - will phase out in the future // Extra (server-side) accepted CBC cipher suites - will phase out in the future
@ -46,62 +55,170 @@ var acceptedCBCCiphers = []uint16{
// known weak algorithms removed. // known weak algorithms removed.
var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...) var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...)
// ServerDefault is a secure-enough TLS configuration for the server TLS configuration. // allTLSVersions lists all the TLS versions and is used by the code that validates
var ServerDefault = tls.Config{ // a uint16 value as a TLS version.
// Avoid fallback to SSL protocols < TLS1.0 var allTLSVersions = map[uint16]struct{}{
MinVersion: tls.VersionTLS10, tls.VersionSSL30: {},
PreferServerCipherSuites: true, tls.VersionTLS10: {},
CipherSuites: DefaultServerAcceptedCiphers, tls.VersionTLS11: {},
tls.VersionTLS12: {},
} }
// ClientDefault is a secure-enough TLS configuration for the client TLS configuration. // ServerDefault returns a secure-enough TLS configuration for the server TLS configuration.
var ClientDefault = tls.Config{ func ServerDefault() *tls.Config {
// Prefer TLS1.2 as the client minimum return &tls.Config{
MinVersion: tls.VersionTLS12, // Avoid fallback to SSL protocols < TLS1.0
CipherSuites: clientCipherSuites, MinVersion: tls.VersionTLS10,
PreferServerCipherSuites: true,
CipherSuites: DefaultServerAcceptedCiphers,
}
}
// ClientDefault returns a secure-enough TLS configuration for the client TLS configuration.
func ClientDefault() *tls.Config {
return &tls.Config{
// Prefer TLS1.2 as the client minimum
MinVersion: tls.VersionTLS12,
CipherSuites: clientCipherSuites,
}
} }
// certPool returns an X.509 certificate pool from `caFile`, the certificate file. // certPool returns an X.509 certificate pool from `caFile`, the certificate file.
func certPool(caFile string) (*x509.CertPool, error) { func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) {
// If we should verify the server, we need to load a trusted ca // If we should verify the server, we need to load a trusted ca
certPool := x509.NewCertPool() var (
certPool *x509.CertPool
err error
)
if exclusivePool {
certPool = x509.NewCertPool()
} else {
certPool, err = SystemCertPool()
if err != nil {
return nil, fmt.Errorf("failed to read system certificates: %v", err)
}
}
pem, err := ioutil.ReadFile(caFile) pem, err := ioutil.ReadFile(caFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("Could not read CA certificate %q: %v", caFile, err) return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err)
} }
if !certPool.AppendCertsFromPEM(pem) { if !certPool.AppendCertsFromPEM(pem) {
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile) return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile)
} }
logrus.Debugf("Trusting %d certs", len(certPool.Subjects()))
return certPool, nil return certPool, nil
} }
// isValidMinVersion checks that the input value is a valid tls minimum version
func isValidMinVersion(version uint16) bool {
_, ok := allTLSVersions[version]
return ok
}
// adjustMinVersion sets the MinVersion on `config`, the input configuration.
// It assumes the current MinVersion on the `config` is the lowest allowed.
func adjustMinVersion(options Options, config *tls.Config) error {
if options.MinVersion > 0 {
if !isValidMinVersion(options.MinVersion) {
return fmt.Errorf("Invalid minimum TLS version: %x", options.MinVersion)
}
if options.MinVersion < config.MinVersion {
return fmt.Errorf("Requested minimum TLS version is too low. Should be at-least: %x", config.MinVersion)
}
config.MinVersion = options.MinVersion
}
return nil
}
// IsErrEncryptedKey returns true if the 'err' is an error of incorrect
// password when tryin to decrypt a TLS private key
func IsErrEncryptedKey(err error) bool {
return errors.Cause(err) == x509.IncorrectPasswordError
}
// getPrivateKey returns the private key in 'keyBytes', in PEM-encoded format.
// If the private key is encrypted, 'passphrase' is used to decrypted the
// private key.
func getPrivateKey(keyBytes []byte, passphrase string) ([]byte, error) {
// this section makes some small changes to code from notary/tuf/utils/x509.go
pemBlock, _ := pem.Decode(keyBytes)
if pemBlock == nil {
return nil, fmt.Errorf("no valid private key found")
}
var err error
if x509.IsEncryptedPEMBlock(pemBlock) {
keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(passphrase))
if err != nil {
return nil, errors.Wrap(err, "private key is encrypted, but could not decrypt it")
}
keyBytes = pem.EncodeToMemory(&pem.Block{Type: pemBlock.Type, Bytes: keyBytes})
}
return keyBytes, nil
}
// getCert returns a Certificate from the CertFile and KeyFile in 'options',
// if the key is encrypted, the Passphrase in 'options' will be used to
// decrypt it.
func getCert(options Options) ([]tls.Certificate, error) {
if options.CertFile == "" && options.KeyFile == "" {
return nil, nil
}
errMessage := "Could not load X509 key pair"
cert, err := ioutil.ReadFile(options.CertFile)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
prKeyBytes, err := ioutil.ReadFile(options.KeyFile)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
prKeyBytes, err = getPrivateKey(prKeyBytes, options.Passphrase)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
tlsCert, err := tls.X509KeyPair(cert, prKeyBytes)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
return []tls.Certificate{tlsCert}, nil
}
// Client returns a TLS configuration meant to be used by a client. // Client returns a TLS configuration meant to be used by a client.
func Client(options Options) (*tls.Config, error) { func Client(options Options) (*tls.Config, error) {
tlsConfig := ClientDefault tlsConfig := ClientDefault()
tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
if !options.InsecureSkipVerify && options.CAFile != "" { if !options.InsecureSkipVerify && options.CAFile != "" {
CAs, err := certPool(options.CAFile) CAs, err := certPool(options.CAFile, options.ExclusiveRootPools)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tlsConfig.RootCAs = CAs tlsConfig.RootCAs = CAs
} }
if options.CertFile != "" || options.KeyFile != "" { tlsCerts, err := getCert(options)
tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile) if err != nil {
if err != nil { return nil, err
return nil, fmt.Errorf("Could not load X509 key pair: %v. Make sure the key is not encrypted", err) }
} tlsConfig.Certificates = tlsCerts
tlsConfig.Certificates = []tls.Certificate{tlsCert}
if err := adjustMinVersion(options, tlsConfig); err != nil {
return nil, err
} }
return &tlsConfig, nil return tlsConfig, nil
} }
// Server returns a TLS configuration meant to be used by a server. // Server returns a TLS configuration meant to be used by a server.
func Server(options Options) (*tls.Config, error) { func Server(options Options) (*tls.Config, error) {
tlsConfig := ServerDefault tlsConfig := ServerDefault()
tlsConfig.ClientAuth = options.ClientAuth tlsConfig.ClientAuth = options.ClientAuth
tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile) tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
if err != nil { if err != nil {
@ -111,12 +228,17 @@ func Server(options Options) (*tls.Config, error) {
return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err) return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err)
} }
tlsConfig.Certificates = []tls.Certificate{tlsCert} tlsConfig.Certificates = []tls.Certificate{tlsCert}
if options.ClientAuth >= tls.VerifyClientCertIfGiven { if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CAFile != "" {
CAs, err := certPool(options.CAFile) CAs, err := certPool(options.CAFile, options.ExclusiveRootPools)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tlsConfig.ClientCAs = CAs tlsConfig.ClientCAs = CAs
} }
return &tlsConfig, nil
if err := adjustMinVersion(options, tlsConfig); err != nil {
return nil, err
}
return tlsConfig, nil
} }

3
vendor/github.com/golang/protobuf/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

3
vendor/github.com/golang/protobuf/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

View File

@ -39,5 +39,5 @@ test: install generate-test-pbs
generate-test-pbs: generate-test-pbs:
make install make install
make -C testdata make -C testdata
protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata:. proto3_proto/proto3.proto protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto
make make

View File

@ -84,9 +84,15 @@ func mergeStruct(out, in reflect.Value) {
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i]) mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
} }
if emIn, ok := in.Addr().Interface().(extendableProto); ok { if emIn, ok := extendable(in.Addr().Interface()); ok {
emOut := out.Addr().Interface().(extendableProto) emOut, _ := extendable(out.Addr().Interface())
mergeExtension(emOut.ExtensionMap(), emIn.ExtensionMap()) mIn, muIn := emIn.extensionsRead()
if mIn != nil {
mOut := emOut.extensionsWrite()
muIn.Lock()
mergeExtension(mOut, mIn)
muIn.Unlock()
}
} }
uf := in.FieldByName("XXX_unrecognized") uf := in.FieldByName("XXX_unrecognized")

View File

@ -61,7 +61,6 @@ var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for
// int32, int64, uint32, uint64, bool, and enum // int32, int64, uint32, uint64, bool, and enum
// protocol buffer types. // protocol buffer types.
func DecodeVarint(buf []byte) (x uint64, n int) { func DecodeVarint(buf []byte) (x uint64, n int) {
// x, n already 0
for shift := uint(0); shift < 64; shift += 7 { for shift := uint(0); shift < 64; shift += 7 {
if n >= len(buf) { if n >= len(buf) {
return 0, 0 return 0, 0
@ -78,13 +77,7 @@ func DecodeVarint(buf []byte) (x uint64, n int) {
return 0, 0 return 0, 0
} }
// DecodeVarint reads a varint-encoded integer from the Buffer. func (p *Buffer) decodeVarintSlow() (x uint64, err error) {
// This is the format for the
// int32, int64, uint32, uint64, bool, and enum
// protocol buffer types.
func (p *Buffer) DecodeVarint() (x uint64, err error) {
// x, err already 0
i := p.index i := p.index
l := len(p.buf) l := len(p.buf)
@ -107,6 +100,107 @@ func (p *Buffer) DecodeVarint() (x uint64, err error) {
return return
} }
// DecodeVarint reads a varint-encoded integer from the Buffer.
// This is the format for the
// int32, int64, uint32, uint64, bool, and enum
// protocol buffer types.
func (p *Buffer) DecodeVarint() (x uint64, err error) {
i := p.index
buf := p.buf
if i >= len(buf) {
return 0, io.ErrUnexpectedEOF
} else if buf[i] < 0x80 {
p.index++
return uint64(buf[i]), nil
} else if len(buf)-i < 10 {
return p.decodeVarintSlow()
}
var b uint64
// we already checked the first byte
x = uint64(buf[i]) - 0x80
i++
b = uint64(buf[i])
i++
x += b << 7
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 7
b = uint64(buf[i])
i++
x += b << 14
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 14
b = uint64(buf[i])
i++
x += b << 21
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 21
b = uint64(buf[i])
i++
x += b << 28
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 28
b = uint64(buf[i])
i++
x += b << 35
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 35
b = uint64(buf[i])
i++
x += b << 42
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 42
b = uint64(buf[i])
i++
x += b << 49
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 49
b = uint64(buf[i])
i++
x += b << 56
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 56
b = uint64(buf[i])
i++
x += b << 63
if b&0x80 == 0 {
goto done
}
// x -= 0x80 << 63 // Always zero.
return 0, errOverflow
done:
p.index = i
return x, nil
}
// DecodeFixed64 reads a 64-bit integer from the Buffer. // DecodeFixed64 reads a 64-bit integer from the Buffer.
// This is the format for the // This is the format for the
// fixed64, sfixed64, and double protocol buffer types. // fixed64, sfixed64, and double protocol buffer types.
@ -340,6 +434,8 @@ func (p *Buffer) DecodeGroup(pb Message) error {
// Buffer and places the decoded result in pb. If the struct // Buffer and places the decoded result in pb. If the struct
// underlying pb does not match the data in the buffer, the results can be // underlying pb does not match the data in the buffer, the results can be
// unpredictable. // unpredictable.
//
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
func (p *Buffer) Unmarshal(pb Message) error { func (p *Buffer) Unmarshal(pb Message) error {
// If the object can unmarshal itself, let it. // If the object can unmarshal itself, let it.
if u, ok := pb.(Unmarshaler); ok { if u, ok := pb.(Unmarshaler); ok {
@ -378,6 +474,11 @@ func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group
wire := int(u & 0x7) wire := int(u & 0x7)
if wire == WireEndGroup { if wire == WireEndGroup {
if is_group { if is_group {
if required > 0 {
// Not enough information to determine the exact field.
// (See below.)
return &RequiredNotSetError{"{Unknown}"}
}
return nil // input is satisfied return nil // input is satisfied
} }
return fmt.Errorf("proto: %s: wiretype end group for non-group", st) return fmt.Errorf("proto: %s: wiretype end group for non-group", st)
@ -390,11 +491,12 @@ func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group
if !ok { if !ok {
// Maybe it's an extension? // Maybe it's an extension?
if prop.extendable { if prop.extendable {
if e := structPointer_Interface(base, st).(extendableProto); isExtensionField(e, int32(tag)) { if e, _ := extendable(structPointer_Interface(base, st)); isExtensionField(e, int32(tag)) {
if err = o.skip(st, tag, wire); err == nil { if err = o.skip(st, tag, wire); err == nil {
ext := e.ExtensionMap()[int32(tag)] // may be missing extmap := e.extensionsWrite()
ext := extmap[int32(tag)] // may be missing
ext.enc = append(ext.enc, o.buf[oi:o.index]...) ext.enc = append(ext.enc, o.buf[oi:o.index]...)
e.ExtensionMap()[int32(tag)] = ext extmap[int32(tag)] = ext
} }
continue continue
} }
@ -768,10 +870,11 @@ func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
} }
} }
keyelem, valelem := keyptr.Elem(), valptr.Elem() keyelem, valelem := keyptr.Elem(), valptr.Elem()
if !keyelem.IsValid() || !valelem.IsValid() { if !keyelem.IsValid() {
// We did not decode the key or the value in the map entry. keyelem = reflect.Zero(p.mtype.Key())
// Either way, it's an invalid map entry. }
return fmt.Errorf("proto: bad map data: missing key/val") if !valelem.IsValid() {
valelem = reflect.Zero(p.mtype.Elem())
} }
v.SetMapIndex(keyelem, valelem) v.SetMapIndex(keyelem, valelem)

View File

@ -64,8 +64,16 @@ var (
// a struct with a repeated field containing a nil element. // a struct with a repeated field containing a nil element.
errRepeatedHasNil = errors.New("proto: repeated field has nil element") errRepeatedHasNil = errors.New("proto: repeated field has nil element")
// errOneofHasNil is the error returned if Marshal is called with
// a struct with a oneof field containing a nil element.
errOneofHasNil = errors.New("proto: oneof field has nil value")
// ErrNil is the error returned if Marshal is called with nil. // ErrNil is the error returned if Marshal is called with nil.
ErrNil = errors.New("proto: Marshal called with nil") ErrNil = errors.New("proto: Marshal called with nil")
// ErrTooLarge is the error returned if Marshal is called with a
// message that encodes to >2GB.
ErrTooLarge = errors.New("proto: message encodes to over 2 GB")
) )
// The fundamental encoders that put bytes on the wire. // The fundamental encoders that put bytes on the wire.
@ -74,6 +82,10 @@ var (
const maxVarintBytes = 10 // maximum length of a varint const maxVarintBytes = 10 // maximum length of a varint
// maxMarshalSize is the largest allowed size of an encoded protobuf,
// since C++ and Java use signed int32s for the size.
const maxMarshalSize = 1<<31 - 1
// EncodeVarint returns the varint encoding of x. // EncodeVarint returns the varint encoding of x.
// This is the format for the // This is the format for the
// int32, int64, uint32, uint64, bool, and enum // int32, int64, uint32, uint64, bool, and enum
@ -162,11 +174,11 @@ func sizeFixed32(x uint64) int {
// This is the format used for the sint64 protocol buffer type. // This is the format used for the sint64 protocol buffer type.
func (p *Buffer) EncodeZigzag64(x uint64) error { func (p *Buffer) EncodeZigzag64(x uint64) error {
// use signed number to get arithmetic right shift. // use signed number to get arithmetic right shift.
return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) return p.EncodeVarint((x << 1) ^ uint64((int64(x) >> 63)))
} }
func sizeZigzag64(x uint64) int { func sizeZigzag64(x uint64) int {
return sizeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) return sizeVarint((x << 1) ^ uint64((int64(x) >> 63)))
} }
// EncodeZigzag32 writes a zigzag-encoded 32-bit integer // EncodeZigzag32 writes a zigzag-encoded 32-bit integer
@ -222,10 +234,6 @@ func Marshal(pb Message) ([]byte, error) {
} }
p := NewBuffer(nil) p := NewBuffer(nil)
err := p.Marshal(pb) err := p.Marshal(pb)
var state errorState
if err != nil && !state.shouldContinue(err, nil) {
return nil, err
}
if p.buf == nil && err == nil { if p.buf == nil && err == nil {
// Return a non-nil slice on success. // Return a non-nil slice on success.
return []byte{}, nil return []byte{}, nil
@ -254,11 +262,8 @@ func (p *Buffer) Marshal(pb Message) error {
// Can the object marshal itself? // Can the object marshal itself?
if m, ok := pb.(Marshaler); ok { if m, ok := pb.(Marshaler); ok {
data, err := m.Marshal() data, err := m.Marshal()
if err != nil {
return err
}
p.buf = append(p.buf, data...) p.buf = append(p.buf, data...)
return nil return err
} }
t, base, err := getbase(pb) t, base, err := getbase(pb)
@ -270,9 +275,12 @@ func (p *Buffer) Marshal(pb Message) error {
} }
if collectStats { if collectStats {
stats.Encode++ (stats).Encode++ // Parens are to work around a goimports bug.
} }
if len(p.buf) > maxMarshalSize {
return ErrTooLarge
}
return err return err
} }
@ -294,7 +302,7 @@ func Size(pb Message) (n int) {
} }
if collectStats { if collectStats {
stats.Size++ (stats).Size++ // Parens are to work around a goimports bug.
} }
return return
@ -999,7 +1007,6 @@ func size_slice_struct_message(p *Properties, base structPointer) (n int) {
if p.isMarshaler { if p.isMarshaler {
m := structPointer_Interface(structp, p.stype).(Marshaler) m := structPointer_Interface(structp, p.stype).(Marshaler)
data, _ := m.Marshal() data, _ := m.Marshal()
n += len(p.tagcode)
n += sizeRawBytes(data) n += sizeRawBytes(data)
continue continue
} }
@ -1058,10 +1065,32 @@ func size_slice_struct_group(p *Properties, base structPointer) (n int) {
// Encode an extension map. // Encode an extension map.
func (o *Buffer) enc_map(p *Properties, base structPointer) error { func (o *Buffer) enc_map(p *Properties, base structPointer) error {
v := *structPointer_ExtMap(base, p.field) exts := structPointer_ExtMap(base, p.field)
if err := encodeExtensionMap(v); err != nil { if err := encodeExtensionsMap(*exts); err != nil {
return err return err
} }
return o.enc_map_body(*exts)
}
func (o *Buffer) enc_exts(p *Properties, base structPointer) error {
exts := structPointer_Extensions(base, p.field)
v, mu := exts.extensionsRead()
if v == nil {
return nil
}
mu.Lock()
defer mu.Unlock()
if err := encodeExtensionsMap(v); err != nil {
return err
}
return o.enc_map_body(v)
}
func (o *Buffer) enc_map_body(v map[int32]Extension) error {
// Fast-path for common cases: zero or one extensions. // Fast-path for common cases: zero or one extensions.
if len(v) <= 1 { if len(v) <= 1 {
for _, e := range v { for _, e := range v {
@ -1084,8 +1113,13 @@ func (o *Buffer) enc_map(p *Properties, base structPointer) error {
} }
func size_map(p *Properties, base structPointer) int { func size_map(p *Properties, base structPointer) int {
v := *structPointer_ExtMap(base, p.field) v := structPointer_ExtMap(base, p.field)
return sizeExtensionMap(v) return extensionsMapSize(*v)
}
func size_exts(p *Properties, base structPointer) int {
v := structPointer_Extensions(base, p.field)
return extensionsSize(v)
} }
// Encode a map field. // Encode a map field.
@ -1114,7 +1148,7 @@ func (o *Buffer) enc_new_map(p *Properties, base structPointer) error {
if err := p.mkeyprop.enc(o, p.mkeyprop, keybase); err != nil { if err := p.mkeyprop.enc(o, p.mkeyprop, keybase); err != nil {
return err return err
} }
if err := p.mvalprop.enc(o, p.mvalprop, valbase); err != nil { if err := p.mvalprop.enc(o, p.mvalprop, valbase); err != nil && err != ErrNil {
return err return err
} }
return nil return nil
@ -1124,11 +1158,6 @@ func (o *Buffer) enc_new_map(p *Properties, base structPointer) error {
for _, key := range v.MapKeys() { for _, key := range v.MapKeys() {
val := v.MapIndex(key) val := v.MapIndex(key)
// The only illegal map entry values are nil message pointers.
if val.Kind() == reflect.Ptr && val.IsNil() {
return errors.New("proto: map has nil element")
}
keycopy.Set(key) keycopy.Set(key)
valcopy.Set(val) valcopy.Set(val)
@ -1216,13 +1245,18 @@ func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error {
return err return err
} }
} }
if len(o.buf) > maxMarshalSize {
return ErrTooLarge
}
} }
} }
// Do oneof fields. // Do oneof fields.
if prop.oneofMarshaler != nil { if prop.oneofMarshaler != nil {
m := structPointer_Interface(base, prop.stype).(Message) m := structPointer_Interface(base, prop.stype).(Message)
if err := prop.oneofMarshaler(m, o); err != nil { if err := prop.oneofMarshaler(m, o); err == ErrNil {
return errOneofHasNil
} else if err != nil {
return err return err
} }
} }
@ -1230,6 +1264,9 @@ func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error {
// Add unrecognized fields at the end. // Add unrecognized fields at the end.
if prop.unrecField.IsValid() { if prop.unrecField.IsValid() {
v := *structPointer_Bytes(base, prop.unrecField) v := *structPointer_Bytes(base, prop.unrecField)
if len(o.buf)+len(v) > maxMarshalSize {
return ErrTooLarge
}
if len(v) > 0 { if len(v) > 0 {
o.buf = append(o.buf, v...) o.buf = append(o.buf, v...)
} }

View File

@ -54,13 +54,17 @@ Equality is defined in this way:
in a proto3 .proto file, fields are not "set"; specifically, in a proto3 .proto file, fields are not "set"; specifically,
zero length proto3 "bytes" fields are equal (nil == {}). zero length proto3 "bytes" fields are equal (nil == {}).
- Two repeated fields are equal iff their lengths are the same, - Two repeated fields are equal iff their lengths are the same,
and their corresponding elements are equal (a "bytes" field, and their corresponding elements are equal. Note a "bytes" field,
although represented by []byte, is not a repeated field) although represented by []byte, is not a repeated field and the
rule for the scalar fields described above applies.
- Two unset fields are equal. - Two unset fields are equal.
- Two unknown field sets are equal if their current - Two unknown field sets are equal if their current
encoded state is equal. encoded state is equal.
- Two extension sets are equal iff they have corresponding - Two extension sets are equal iff they have corresponding
elements that are pairwise equal. elements that are pairwise equal.
- Two map fields are equal iff their lengths are the same,
and they contain the same set of elements. Zero-length map
fields are equal.
- Every other combination of things are not equal. - Every other combination of things are not equal.
The return value is undefined if a and b are not protocol buffers. The return value is undefined if a and b are not protocol buffers.
@ -121,9 +125,16 @@ func equalStruct(v1, v2 reflect.Value) bool {
} }
} }
if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() {
em2 := v2.FieldByName("XXX_InternalExtensions")
if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) {
return false
}
}
if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() { if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
em2 := v2.FieldByName("XXX_extensions") em2 := v2.FieldByName("XXX_extensions")
if !equalExtensions(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) { if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
return false return false
} }
} }
@ -184,6 +195,13 @@ func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
} }
return true return true
case reflect.Ptr: case reflect.Ptr:
// Maps may have nil values in them, so check for nil.
if v1.IsNil() && v2.IsNil() {
return true
}
if v1.IsNil() != v2.IsNil() {
return false
}
return equalAny(v1.Elem(), v2.Elem(), prop) return equalAny(v1.Elem(), v2.Elem(), prop)
case reflect.Slice: case reflect.Slice:
if v1.Type().Elem().Kind() == reflect.Uint8 { if v1.Type().Elem().Kind() == reflect.Uint8 {
@ -223,8 +241,14 @@ func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
} }
// base is the struct type that the extensions are based on. // base is the struct type that the extensions are based on.
// em1 and em2 are extension maps. // x1 and x2 are InternalExtensions.
func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool { func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool {
em1, _ := x1.extensionsRead()
em2, _ := x2.extensionsRead()
return equalExtMap(base, em1, em2)
}
func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
if len(em1) != len(em2) { if len(em1) != len(em2) {
return false return false
} }

View File

@ -52,14 +52,99 @@ type ExtensionRange struct {
Start, End int32 // both inclusive Start, End int32 // both inclusive
} }
// extendableProto is an interface implemented by any protocol buffer that may be extended. // extendableProto is an interface implemented by any protocol buffer generated by the current
// proto compiler that may be extended.
type extendableProto interface { type extendableProto interface {
Message
ExtensionRangeArray() []ExtensionRange
extensionsWrite() map[int32]Extension
extensionsRead() (map[int32]Extension, sync.Locker)
}
// extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous
// version of the proto compiler that may be extended.
type extendableProtoV1 interface {
Message Message
ExtensionRangeArray() []ExtensionRange ExtensionRangeArray() []ExtensionRange
ExtensionMap() map[int32]Extension ExtensionMap() map[int32]Extension
} }
// extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto.
type extensionAdapter struct {
extendableProtoV1
}
func (e extensionAdapter) extensionsWrite() map[int32]Extension {
return e.ExtensionMap()
}
func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) {
return e.ExtensionMap(), notLocker{}
}
// notLocker is a sync.Locker whose Lock and Unlock methods are nops.
type notLocker struct{}
func (n notLocker) Lock() {}
func (n notLocker) Unlock() {}
// extendable returns the extendableProto interface for the given generated proto message.
// If the proto message has the old extension format, it returns a wrapper that implements
// the extendableProto interface.
func extendable(p interface{}) (extendableProto, bool) {
if ep, ok := p.(extendableProto); ok {
return ep, ok
}
if ep, ok := p.(extendableProtoV1); ok {
return extensionAdapter{ep}, ok
}
return nil, false
}
// XXX_InternalExtensions is an internal representation of proto extensions.
//
// Each generated message struct type embeds an anonymous XXX_InternalExtensions field,
// thus gaining the unexported 'extensions' method, which can be called only from the proto package.
//
// The methods of XXX_InternalExtensions are not concurrency safe in general,
// but calls to logically read-only methods such as has and get may be executed concurrently.
type XXX_InternalExtensions struct {
// The struct must be indirect so that if a user inadvertently copies a
// generated message and its embedded XXX_InternalExtensions, they
// avoid the mayhem of a copied mutex.
//
// The mutex serializes all logically read-only operations to p.extensionMap.
// It is up to the client to ensure that write operations to p.extensionMap are
// mutually exclusive with other accesses.
p *struct {
mu sync.Mutex
extensionMap map[int32]Extension
}
}
// extensionsWrite returns the extension map, creating it on first use.
func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension {
if e.p == nil {
e.p = new(struct {
mu sync.Mutex
extensionMap map[int32]Extension
})
e.p.extensionMap = make(map[int32]Extension)
}
return e.p.extensionMap
}
// extensionsRead returns the extensions map for read-only use. It may be nil.
// The caller must hold the returned mutex's lock when accessing Elements within the map.
func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) {
if e.p == nil {
return nil, nil
}
return e.p.extensionMap, &e.p.mu
}
var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem() var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
var extendableProtoV1Type = reflect.TypeOf((*extendableProtoV1)(nil)).Elem()
// ExtensionDesc represents an extension specification. // ExtensionDesc represents an extension specification.
// Used in generated code from the protocol compiler. // Used in generated code from the protocol compiler.
@ -69,6 +154,7 @@ type ExtensionDesc struct {
Field int32 // field number Field int32 // field number
Name string // fully-qualified name of extension, for text formatting Name string // fully-qualified name of extension, for text formatting
Tag string // protobuf tag style Tag string // protobuf tag style
Filename string // name of the file in which the extension is defined
} }
func (ed *ExtensionDesc) repeated() bool { func (ed *ExtensionDesc) repeated() bool {
@ -92,8 +178,13 @@ type Extension struct {
} }
// SetRawExtension is for testing only. // SetRawExtension is for testing only.
func SetRawExtension(base extendableProto, id int32, b []byte) { func SetRawExtension(base Message, id int32, b []byte) {
base.ExtensionMap()[id] = Extension{enc: b} epb, ok := extendable(base)
if !ok {
return
}
extmap := epb.extensionsWrite()
extmap[id] = Extension{enc: b}
} }
// isExtensionField returns true iff the given field number is in an extension range. // isExtensionField returns true iff the given field number is in an extension range.
@ -108,8 +199,12 @@ func isExtensionField(pb extendableProto, field int32) bool {
// checkExtensionTypes checks that the given extension is valid for pb. // checkExtensionTypes checks that the given extension is valid for pb.
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error { func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
var pbi interface{} = pb
// Check the extended type. // Check the extended type.
if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b { if ea, ok := pbi.(extensionAdapter); ok {
pbi = ea.extendableProtoV1
}
if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b {
return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String()) return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String())
} }
// Check the range. // Check the range.
@ -155,8 +250,19 @@ func extensionProperties(ed *ExtensionDesc) *Properties {
return prop return prop
} }
// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m. // encode encodes any unmarshaled (unencoded) extensions in e.
func encodeExtensionMap(m map[int32]Extension) error { func encodeExtensions(e *XXX_InternalExtensions) error {
m, mu := e.extensionsRead()
if m == nil {
return nil // fast path
}
mu.Lock()
defer mu.Unlock()
return encodeExtensionsMap(m)
}
// encode encodes any unmarshaled (unencoded) extensions in e.
func encodeExtensionsMap(m map[int32]Extension) error {
for k, e := range m { for k, e := range m {
if e.value == nil || e.desc == nil { if e.value == nil || e.desc == nil {
// Extension is only in its encoded form. // Extension is only in its encoded form.
@ -184,7 +290,17 @@ func encodeExtensionMap(m map[int32]Extension) error {
return nil return nil
} }
func sizeExtensionMap(m map[int32]Extension) (n int) { func extensionsSize(e *XXX_InternalExtensions) (n int) {
m, mu := e.extensionsRead()
if m == nil {
return 0
}
mu.Lock()
defer mu.Unlock()
return extensionsMapSize(m)
}
func extensionsMapSize(m map[int32]Extension) (n int) {
for _, e := range m { for _, e := range m {
if e.value == nil || e.desc == nil { if e.value == nil || e.desc == nil {
// Extension is only in its encoded form. // Extension is only in its encoded form.
@ -209,26 +325,51 @@ func sizeExtensionMap(m map[int32]Extension) (n int) {
} }
// HasExtension returns whether the given extension is present in pb. // HasExtension returns whether the given extension is present in pb.
func HasExtension(pb extendableProto, extension *ExtensionDesc) bool { func HasExtension(pb Message, extension *ExtensionDesc) bool {
// TODO: Check types, field numbers, etc.? // TODO: Check types, field numbers, etc.?
_, ok := pb.ExtensionMap()[extension.Field] epb, ok := extendable(pb)
if !ok {
return false
}
extmap, mu := epb.extensionsRead()
if extmap == nil {
return false
}
mu.Lock()
_, ok = extmap[extension.Field]
mu.Unlock()
return ok return ok
} }
// ClearExtension removes the given extension from pb. // ClearExtension removes the given extension from pb.
func ClearExtension(pb extendableProto, extension *ExtensionDesc) { func ClearExtension(pb Message, extension *ExtensionDesc) {
epb, ok := extendable(pb)
if !ok {
return
}
// TODO: Check types, field numbers, etc.? // TODO: Check types, field numbers, etc.?
delete(pb.ExtensionMap(), extension.Field) extmap := epb.extensionsWrite()
delete(extmap, extension.Field)
} }
// GetExtension parses and returns the given extension of pb. // GetExtension parses and returns the given extension of pb.
// If the extension is not present and has no default value it returns ErrMissingExtension. // If the extension is not present and has no default value it returns ErrMissingExtension.
func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) { func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
if err := checkExtensionTypes(pb, extension); err != nil { epb, ok := extendable(pb)
if !ok {
return nil, errors.New("proto: not an extendable proto")
}
if err := checkExtensionTypes(epb, extension); err != nil {
return nil, err return nil, err
} }
emap := pb.ExtensionMap() emap, mu := epb.extensionsRead()
if emap == nil {
return defaultExtensionValue(extension)
}
mu.Lock()
defer mu.Unlock()
e, ok := emap[extension.Field] e, ok := emap[extension.Field]
if !ok { if !ok {
// defaultExtensionValue returns the default value or // defaultExtensionValue returns the default value or
@ -332,10 +473,9 @@ func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
// GetExtensions returns a slice of the extensions present in pb that are also listed in es. // GetExtensions returns a slice of the extensions present in pb that are also listed in es.
// The returned slice has the same length as es; missing extensions will appear as nil elements. // The returned slice has the same length as es; missing extensions will appear as nil elements.
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) { func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
epb, ok := pb.(extendableProto) epb, ok := extendable(pb)
if !ok { if !ok {
err = errors.New("proto: not an extendable proto") return nil, errors.New("proto: not an extendable proto")
return
} }
extensions = make([]interface{}, len(es)) extensions = make([]interface{}, len(es))
for i, e := range es { for i, e := range es {
@ -350,9 +490,44 @@ func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, e
return return
} }
// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order.
// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
// just the Field field, which defines the extension's field number.
func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
epb, ok := extendable(pb)
if !ok {
return nil, fmt.Errorf("proto: %T is not an extendable proto.Message", pb)
}
registeredExtensions := RegisteredExtensions(pb)
emap, mu := epb.extensionsRead()
if emap == nil {
return nil, nil
}
mu.Lock()
defer mu.Unlock()
extensions := make([]*ExtensionDesc, 0, len(emap))
for extid, e := range emap {
desc := e.desc
if desc == nil {
desc = registeredExtensions[extid]
if desc == nil {
desc = &ExtensionDesc{Field: extid}
}
}
extensions = append(extensions, desc)
}
return extensions, nil
}
// SetExtension sets the specified extension of pb to the specified value. // SetExtension sets the specified extension of pb to the specified value.
func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error { func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
if err := checkExtensionTypes(pb, extension); err != nil { epb, ok := extendable(pb)
if !ok {
return errors.New("proto: not an extendable proto")
}
if err := checkExtensionTypes(epb, extension); err != nil {
return err return err
} }
typ := reflect.TypeOf(extension.ExtensionType) typ := reflect.TypeOf(extension.ExtensionType)
@ -368,10 +543,23 @@ func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{
return fmt.Errorf("proto: SetExtension called with nil value of type %T", value) return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
} }
pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value} extmap := epb.extensionsWrite()
extmap[extension.Field] = Extension{desc: extension, value: value}
return nil return nil
} }
// ClearAllExtensions clears all extensions from pb.
func ClearAllExtensions(pb Message) {
epb, ok := extendable(pb)
if !ok {
return
}
m := epb.extensionsWrite()
for k := range m {
delete(m, k)
}
}
// A global registry of extensions. // A global registry of extensions.
// The generated code will register the generated descriptors by calling RegisterExtension. // The generated code will register the generated descriptors by calling RegisterExtension.

View File

@ -73,7 +73,6 @@ for a protocol buffer variable v:
When the .proto file specifies `syntax="proto3"`, there are some differences: When the .proto file specifies `syntax="proto3"`, there are some differences:
- Non-repeated fields of non-message type are values instead of pointers. - Non-repeated fields of non-message type are values instead of pointers.
- Getters are only generated for message and oneof fields.
- Enum types do not get an Enum method. - Enum types do not get an Enum method.
The simplest way to describe this is to see an example. The simplest way to describe this is to see an example.
@ -308,7 +307,7 @@ func GetStats() Stats { return stats }
// temporary Buffer and are fine for most applications. // temporary Buffer and are fine for most applications.
type Buffer struct { type Buffer struct {
buf []byte // encode/decode byte stream buf []byte // encode/decode byte stream
index int // write point index int // read point
// pools of basic types to amortize allocation. // pools of basic types to amortize allocation.
bools []bool bools []bool
@ -889,6 +888,10 @@ func isProto3Zero(v reflect.Value) bool {
return false return false
} }
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package.
const ProtoPackageIsVersion2 = true
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files // ProtoPackageIsVersion1 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package. // to assert that that code is compatible with this version of the proto package.
const ProtoPackageIsVersion1 = true const ProtoPackageIsVersion1 = true

View File

@ -149,9 +149,21 @@ func skipVarint(buf []byte) []byte {
// MarshalMessageSet encodes the extension map represented by m in the message set wire format. // MarshalMessageSet encodes the extension map represented by m in the message set wire format.
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option. // It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
func MarshalMessageSet(m map[int32]Extension) ([]byte, error) { func MarshalMessageSet(exts interface{}) ([]byte, error) {
if err := encodeExtensionMap(m); err != nil { var m map[int32]Extension
return nil, err switch exts := exts.(type) {
case *XXX_InternalExtensions:
if err := encodeExtensions(exts); err != nil {
return nil, err
}
m, _ = exts.extensionsRead()
case map[int32]Extension:
if err := encodeExtensionsMap(exts); err != nil {
return nil, err
}
m = exts
default:
return nil, errors.New("proto: not an extension map")
} }
// Sort extension IDs to provide a deterministic encoding. // Sort extension IDs to provide a deterministic encoding.
@ -178,7 +190,17 @@ func MarshalMessageSet(m map[int32]Extension) ([]byte, error) {
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. // UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option. // It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
func UnmarshalMessageSet(buf []byte, m map[int32]Extension) error { func UnmarshalMessageSet(buf []byte, exts interface{}) error {
var m map[int32]Extension
switch exts := exts.(type) {
case *XXX_InternalExtensions:
m = exts.extensionsWrite()
case map[int32]Extension:
m = exts
default:
return errors.New("proto: not an extension map")
}
ms := new(messageSet) ms := new(messageSet)
if err := Unmarshal(buf, ms); err != nil { if err := Unmarshal(buf, ms); err != nil {
return err return err
@ -209,7 +231,16 @@ func UnmarshalMessageSet(buf []byte, m map[int32]Extension) error {
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format. // MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option. // It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
func MarshalMessageSetJSON(m map[int32]Extension) ([]byte, error) { func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
var m map[int32]Extension
switch exts := exts.(type) {
case *XXX_InternalExtensions:
m, _ = exts.extensionsRead()
case map[int32]Extension:
m = exts
default:
return nil, errors.New("proto: not an extension map")
}
var b bytes.Buffer var b bytes.Buffer
b.WriteByte('{') b.WriteByte('{')
@ -252,7 +283,7 @@ func MarshalMessageSetJSON(m map[int32]Extension) ([]byte, error) {
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format. // UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option. // It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
func UnmarshalMessageSetJSON(buf []byte, m map[int32]Extension) error { func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error {
// Common-case fast path. // Common-case fast path.
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) { if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
return nil return nil

View File

@ -29,7 +29,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build appengine // +build appengine js
// This file contains an implementation of proto field accesses using package reflect. // This file contains an implementation of proto field accesses using package reflect.
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can // It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
@ -139,6 +139,11 @@ func structPointer_StringSlice(p structPointer, f field) *[]string {
return structPointer_ifield(p, f).(*[]string) return structPointer_ifield(p, f).(*[]string)
} }
// Extensions returns the address of an extension map field in the struct.
func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
return structPointer_ifield(p, f).(*XXX_InternalExtensions)
}
// ExtMap returns the address of an extension map field in the struct. // ExtMap returns the address of an extension map field in the struct.
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension { func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
return structPointer_ifield(p, f).(*map[int32]Extension) return structPointer_ifield(p, f).(*map[int32]Extension)

View File

@ -29,7 +29,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build !appengine // +build !appengine,!js
// This file contains the implementation of the proto field accesses using package unsafe. // This file contains the implementation of the proto field accesses using package unsafe.
@ -126,6 +126,10 @@ func structPointer_StringSlice(p structPointer, f field) *[]string {
} }
// ExtMap returns the address of an extension map field in the struct. // ExtMap returns the address of an extension map field in the struct.
func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
return (*XXX_InternalExtensions)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension { func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f))) return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
} }

View File

@ -173,6 +173,7 @@ func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order
type Properties struct { type Properties struct {
Name string // name of the field, for error messages Name string // name of the field, for error messages
OrigName string // original name before protocol compiler (always set) OrigName string // original name before protocol compiler (always set)
JSONName string // name to use for JSON; determined by protoc
Wire string Wire string
WireType int WireType int
Tag int Tag int
@ -229,8 +230,9 @@ func (p *Properties) String() string {
if p.Packed { if p.Packed {
s += ",packed" s += ",packed"
} }
if p.OrigName != p.Name { s += ",name=" + p.OrigName
s += ",name=" + p.OrigName if p.JSONName != p.OrigName {
s += ",json=" + p.JSONName
} }
if p.proto3 { if p.proto3 {
s += ",proto3" s += ",proto3"
@ -310,6 +312,8 @@ func (p *Properties) Parse(s string) {
p.Packed = true p.Packed = true
case strings.HasPrefix(f, "name="): case strings.HasPrefix(f, "name="):
p.OrigName = f[5:] p.OrigName = f[5:]
case strings.HasPrefix(f, "json="):
p.JSONName = f[5:]
case strings.HasPrefix(f, "enum="): case strings.HasPrefix(f, "enum="):
p.Enum = f[5:] p.Enum = f[5:]
case f == "proto3": case f == "proto3":
@ -469,17 +473,13 @@ func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lock
p.dec = (*Buffer).dec_slice_int64 p.dec = (*Buffer).dec_slice_int64
p.packedDec = (*Buffer).dec_slice_packed_int64 p.packedDec = (*Buffer).dec_slice_packed_int64
case reflect.Uint8: case reflect.Uint8:
p.enc = (*Buffer).enc_slice_byte
p.dec = (*Buffer).dec_slice_byte p.dec = (*Buffer).dec_slice_byte
p.size = size_slice_byte if p.proto3 {
// This is a []byte, which is either a bytes field,
// or the value of a map field. In the latter case,
// we always encode an empty []byte, so we should not
// use the proto3 enc/size funcs.
// f == nil iff this is the key/value of a map field.
if p.proto3 && f != nil {
p.enc = (*Buffer).enc_proto3_slice_byte p.enc = (*Buffer).enc_proto3_slice_byte
p.size = size_proto3_slice_byte p.size = size_proto3_slice_byte
} else {
p.enc = (*Buffer).enc_slice_byte
p.size = size_slice_byte
} }
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
switch t2.Bits() { switch t2.Bits() {
@ -678,7 +678,8 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
propertiesMap[t] = prop propertiesMap[t] = prop
// build properties // build properties
prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType) prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType) ||
reflect.PtrTo(t).Implements(extendableProtoV1Type)
prop.unrecField = invalidField prop.unrecField = invalidField
prop.Prop = make([]*Properties, t.NumField()) prop.Prop = make([]*Properties, t.NumField())
prop.order = make([]int, t.NumField()) prop.order = make([]int, t.NumField())
@ -689,15 +690,22 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
name := f.Name name := f.Name
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false) p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
if f.Name == "XXX_extensions" { // special case if f.Name == "XXX_InternalExtensions" { // special case
p.enc = (*Buffer).enc_exts
p.dec = nil // not needed
p.size = size_exts
} else if f.Name == "XXX_extensions" { // special case
p.enc = (*Buffer).enc_map p.enc = (*Buffer).enc_map
p.dec = nil // not needed p.dec = nil // not needed
p.size = size_map p.size = size_map
} } else if f.Name == "XXX_unrecognized" { // special case
if f.Name == "XXX_unrecognized" { // special case
prop.unrecField = toField(&f) prop.unrecField = toField(&f)
} }
oneof := f.Tag.Get("protobuf_oneof") != "" // special case oneof := f.Tag.Get("protobuf_oneof") // special case
if oneof != "" {
// Oneof fields don't use the traditional protobuf tag.
p.OrigName = oneof
}
prop.Prop[i] = p prop.Prop[i] = p
prop.order[i] = i prop.order[i] = i
if debug { if debug {
@ -707,7 +715,7 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
} }
print("\n") print("\n")
} }
if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && !oneof { if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && oneof == "" {
fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]") fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
} }
} }
@ -836,7 +844,29 @@ func RegisterType(x Message, name string) {
} }
// MessageName returns the fully-qualified proto name for the given message type. // MessageName returns the fully-qualified proto name for the given message type.
func MessageName(x Message) string { return revProtoTypes[reflect.TypeOf(x)] } func MessageName(x Message) string {
type xname interface {
XXX_MessageName() string
}
if m, ok := x.(xname); ok {
return m.XXX_MessageName()
}
return revProtoTypes[reflect.TypeOf(x)]
}
// MessageType returns the message type (pointer to struct) for a named message. // MessageType returns the message type (pointer to struct) for a named message.
func MessageType(name string) reflect.Type { return protoTypes[name] } func MessageType(name string) reflect.Type { return protoTypes[name] }
// A registry of all linked proto files.
var (
protoFiles = make(map[string][]byte) // file name => fileDescriptor
)
// RegisterFile is called from generated code and maps from the
// full file name of a .proto file to its compressed FileDescriptorProto.
func RegisterFile(filename string, fileDescriptor []byte) {
protoFiles[filename] = fileDescriptor
}
// FileDescriptor returns the compressed FileDescriptorProto for a .proto file.
func FileDescriptor(filename string) []byte { return protoFiles[filename] }

View File

@ -154,7 +154,7 @@ func (w *textWriter) indent() { w.ind++ }
func (w *textWriter) unindent() { func (w *textWriter) unindent() {
if w.ind == 0 { if w.ind == 0 {
log.Printf("proto: textWriter unindented too far") log.Print("proto: textWriter unindented too far")
return return
} }
w.ind-- w.ind--
@ -175,7 +175,93 @@ type raw interface {
Bytes() []byte Bytes() []byte
} }
func writeStruct(w *textWriter, sv reflect.Value) error { func requiresQuotes(u string) bool {
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
for _, ch := range u {
switch {
case ch == '.' || ch == '/' || ch == '_':
continue
case '0' <= ch && ch <= '9':
continue
case 'A' <= ch && ch <= 'Z':
continue
case 'a' <= ch && ch <= 'z':
continue
default:
return true
}
}
return false
}
// isAny reports whether sv is a google.protobuf.Any message
func isAny(sv reflect.Value) bool {
type wkt interface {
XXX_WellKnownType() string
}
t, ok := sv.Addr().Interface().(wkt)
return ok && t.XXX_WellKnownType() == "Any"
}
// writeProto3Any writes an expanded google.protobuf.Any message.
//
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
// required messages are not linked in).
//
// It returns (true, error) when sv was written in expanded format or an error
// was encountered.
func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
turl := sv.FieldByName("TypeUrl")
val := sv.FieldByName("Value")
if !turl.IsValid() || !val.IsValid() {
return true, errors.New("proto: invalid google.protobuf.Any message")
}
b, ok := val.Interface().([]byte)
if !ok {
return true, errors.New("proto: invalid google.protobuf.Any message")
}
parts := strings.Split(turl.String(), "/")
mt := MessageType(parts[len(parts)-1])
if mt == nil {
return false, nil
}
m := reflect.New(mt.Elem())
if err := Unmarshal(b, m.Interface().(Message)); err != nil {
return false, nil
}
w.Write([]byte("["))
u := turl.String()
if requiresQuotes(u) {
writeString(w, u)
} else {
w.Write([]byte(u))
}
if w.compact {
w.Write([]byte("]:<"))
} else {
w.Write([]byte("]: <\n"))
w.ind++
}
if err := tm.writeStruct(w, m.Elem()); err != nil {
return true, err
}
if w.compact {
w.Write([]byte("> "))
} else {
w.ind--
w.Write([]byte(">\n"))
}
return true, nil
}
func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
if tm.ExpandAny && isAny(sv) {
if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
return err
}
}
st := sv.Type() st := sv.Type()
sprops := GetProperties(st) sprops := GetProperties(st)
for i := 0; i < sv.NumField(); i++ { for i := 0; i < sv.NumField(); i++ {
@ -227,7 +313,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
} }
continue continue
} }
if err := writeAny(w, v, props); err != nil { if err := tm.writeAny(w, v, props); err != nil {
return err return err
} }
if err := w.WriteByte('\n'); err != nil { if err := w.WriteByte('\n'); err != nil {
@ -269,7 +355,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
return err return err
} }
} }
if err := writeAny(w, key, props.mkeyprop); err != nil { if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
return err return err
} }
if err := w.WriteByte('\n'); err != nil { if err := w.WriteByte('\n'); err != nil {
@ -286,7 +372,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
return err return err
} }
} }
if err := writeAny(w, val, props.mvalprop); err != nil { if err := tm.writeAny(w, val, props.mvalprop); err != nil {
return err return err
} }
if err := w.WriteByte('\n'); err != nil { if err := w.WriteByte('\n'); err != nil {
@ -358,7 +444,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
} }
// Enums have a String method, so writeAny will work fine. // Enums have a String method, so writeAny will work fine.
if err := writeAny(w, fv, props); err != nil { if err := tm.writeAny(w, fv, props); err != nil {
return err return err
} }
@ -369,8 +455,8 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
// Extensions (the XXX_extensions field). // Extensions (the XXX_extensions field).
pv := sv.Addr() pv := sv.Addr()
if pv.Type().Implements(extendableProtoType) { if _, ok := extendable(pv.Interface()); ok {
if err := writeExtensions(w, pv); err != nil { if err := tm.writeExtensions(w, pv); err != nil {
return err return err
} }
} }
@ -400,7 +486,7 @@ func writeRaw(w *textWriter, b []byte) error {
} }
// writeAny writes an arbitrary field. // writeAny writes an arbitrary field.
func writeAny(w *textWriter, v reflect.Value, props *Properties) error { func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
v = reflect.Indirect(v) v = reflect.Indirect(v)
// Floats have special cases. // Floats have special cases.
@ -427,7 +513,7 @@ func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
switch v.Kind() { switch v.Kind() {
case reflect.Slice: case reflect.Slice:
// Should only be a []byte; repeated fields are handled in writeStruct. // Should only be a []byte; repeated fields are handled in writeStruct.
if err := writeString(w, string(v.Interface().([]byte))); err != nil { if err := writeString(w, string(v.Bytes())); err != nil {
return err return err
} }
case reflect.String: case reflect.String:
@ -449,15 +535,15 @@ func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
} }
} }
w.indent() w.indent()
if tm, ok := v.Interface().(encoding.TextMarshaler); ok { if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
text, err := tm.MarshalText() text, err := etm.MarshalText()
if err != nil { if err != nil {
return err return err
} }
if _, err = w.Write(text); err != nil { if _, err = w.Write(text); err != nil {
return err return err
} }
} else if err := writeStruct(w, v); err != nil { } else if err := tm.writeStruct(w, v); err != nil {
return err return err
} }
w.unindent() w.unindent()
@ -601,19 +687,24 @@ func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// writeExtensions writes all the extensions in pv. // writeExtensions writes all the extensions in pv.
// pv is assumed to be a pointer to a protocol message struct that is extendable. // pv is assumed to be a pointer to a protocol message struct that is extendable.
func writeExtensions(w *textWriter, pv reflect.Value) error { func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
emap := extensionMaps[pv.Type().Elem()] emap := extensionMaps[pv.Type().Elem()]
ep := pv.Interface().(extendableProto) ep, _ := extendable(pv.Interface())
// Order the extensions by ID. // Order the extensions by ID.
// This isn't strictly necessary, but it will give us // This isn't strictly necessary, but it will give us
// canonical output, which will also make testing easier. // canonical output, which will also make testing easier.
m := ep.ExtensionMap() m, mu := ep.extensionsRead()
if m == nil {
return nil
}
mu.Lock()
ids := make([]int32, 0, len(m)) ids := make([]int32, 0, len(m))
for id := range m { for id := range m {
ids = append(ids, id) ids = append(ids, id)
} }
sort.Sort(int32Slice(ids)) sort.Sort(int32Slice(ids))
mu.Unlock()
for _, extNum := range ids { for _, extNum := range ids {
ext := m[extNum] ext := m[extNum]
@ -636,13 +727,13 @@ func writeExtensions(w *textWriter, pv reflect.Value) error {
// Repeated extensions will appear as a slice. // Repeated extensions will appear as a slice.
if !desc.repeated() { if !desc.repeated() {
if err := writeExtension(w, desc.Name, pb); err != nil { if err := tm.writeExtension(w, desc.Name, pb); err != nil {
return err return err
} }
} else { } else {
v := reflect.ValueOf(pb) v := reflect.ValueOf(pb)
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
if err := writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil { if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
return err return err
} }
} }
@ -651,7 +742,7 @@ func writeExtensions(w *textWriter, pv reflect.Value) error {
return nil return nil
} }
func writeExtension(w *textWriter, name string, pb interface{}) error { func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil { if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
return err return err
} }
@ -660,7 +751,7 @@ func writeExtension(w *textWriter, name string, pb interface{}) error {
return err return err
} }
} }
if err := writeAny(w, reflect.ValueOf(pb), nil); err != nil { if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
return err return err
} }
if err := w.WriteByte('\n'); err != nil { if err := w.WriteByte('\n'); err != nil {
@ -685,7 +776,15 @@ func (w *textWriter) writeIndent() {
w.complete = false w.complete = false
} }
func marshalText(w io.Writer, pb Message, compact bool) error { // TextMarshaler is a configurable text format marshaler.
type TextMarshaler struct {
Compact bool // use compact text format (one line).
ExpandAny bool // expand google.protobuf.Any messages of known types
}
// Marshal writes a given protocol buffer in text format.
// The only errors returned are from w.
func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
val := reflect.ValueOf(pb) val := reflect.ValueOf(pb)
if pb == nil || val.IsNil() { if pb == nil || val.IsNil() {
w.Write([]byte("<nil>")) w.Write([]byte("<nil>"))
@ -700,11 +799,11 @@ func marshalText(w io.Writer, pb Message, compact bool) error {
aw := &textWriter{ aw := &textWriter{
w: ww, w: ww,
complete: true, complete: true,
compact: compact, compact: tm.Compact,
} }
if tm, ok := pb.(encoding.TextMarshaler); ok { if etm, ok := pb.(encoding.TextMarshaler); ok {
text, err := tm.MarshalText() text, err := etm.MarshalText()
if err != nil { if err != nil {
return err return err
} }
@ -718,7 +817,7 @@ func marshalText(w io.Writer, pb Message, compact bool) error {
} }
// Dereference the received pointer so we don't have outer < and >. // Dereference the received pointer so we don't have outer < and >.
v := reflect.Indirect(val) v := reflect.Indirect(val)
if err := writeStruct(aw, v); err != nil { if err := tm.writeStruct(aw, v); err != nil {
return err return err
} }
if bw != nil { if bw != nil {
@ -727,25 +826,29 @@ func marshalText(w io.Writer, pb Message, compact bool) error {
return nil return nil
} }
// Text is the same as Marshal, but returns the string directly.
func (tm *TextMarshaler) Text(pb Message) string {
var buf bytes.Buffer
tm.Marshal(&buf, pb)
return buf.String()
}
var (
defaultTextMarshaler = TextMarshaler{}
compactTextMarshaler = TextMarshaler{Compact: true}
)
// TODO: consider removing some of the Marshal functions below.
// MarshalText writes a given protocol buffer in text format. // MarshalText writes a given protocol buffer in text format.
// The only errors returned are from w. // The only errors returned are from w.
func MarshalText(w io.Writer, pb Message) error { func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
return marshalText(w, pb, false)
}
// MarshalTextString is the same as MarshalText, but returns the string directly. // MarshalTextString is the same as MarshalText, but returns the string directly.
func MarshalTextString(pb Message) string { func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
var buf bytes.Buffer
marshalText(&buf, pb, false)
return buf.String()
}
// CompactText writes a given protocol buffer in compact text format (one line). // CompactText writes a given protocol buffer in compact text format (one line).
func CompactText(w io.Writer, pb Message) error { return marshalText(w, pb, true) } func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
// CompactTextString is the same as CompactText, but returns the string directly. // CompactTextString is the same as CompactText, but returns the string directly.
func CompactTextString(pb Message) string { func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
var buf bytes.Buffer
marshalText(&buf, pb, true)
return buf.String()
}

View File

@ -44,6 +44,9 @@ import (
"unicode/utf8" "unicode/utf8"
) )
// Error string emitted when deserializing Any and fields are already set
const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set"
type ParseError struct { type ParseError struct {
Message string Message string
Line int // 1-based line number Line int // 1-based line number
@ -163,7 +166,7 @@ func (p *textParser) advance() {
p.cur.offset, p.cur.line = p.offset, p.line p.cur.offset, p.cur.line = p.offset, p.line
p.cur.unquoted = "" p.cur.unquoted = ""
switch p.s[0] { switch p.s[0] {
case '<', '>', '{', '}', ':', '[', ']', ';', ',': case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
// Single symbol // Single symbol
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
case '"', '\'': case '"', '\'':
@ -451,7 +454,10 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
fieldSet := make(map[string]bool) fieldSet := make(map[string]bool)
// A struct is a sequence of "name: value", terminated by one of // A struct is a sequence of "name: value", terminated by one of
// '>' or '}', or the end of the input. A name may also be // '>' or '}', or the end of the input. A name may also be
// "[extension]". // "[extension]" or "[type/url]".
//
// The whole struct can also be an expanded Any message, like:
// [type/url] < ... struct contents ... >
for { for {
tok := p.next() tok := p.next()
if tok.err != nil { if tok.err != nil {
@ -461,33 +467,74 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
break break
} }
if tok.value == "[" { if tok.value == "[" {
// Looks like an extension. // Looks like an extension or an Any.
// //
// TODO: Check whether we need to handle // TODO: Check whether we need to handle
// namespace rooted names (e.g. ".something.Foo"). // namespace rooted names (e.g. ".something.Foo").
tok = p.next() extName, err := p.consumeExtName()
if tok.err != nil { if err != nil {
return tok.err return err
} }
if s := strings.LastIndex(extName, "/"); s >= 0 {
// If it contains a slash, it's an Any type URL.
messageName := extName[s+1:]
mt := MessageType(messageName)
if mt == nil {
return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
}
tok = p.next()
if tok.err != nil {
return tok.err
}
// consume an optional colon
if tok.value == ":" {
tok = p.next()
if tok.err != nil {
return tok.err
}
}
var terminator string
switch tok.value {
case "<":
terminator = ">"
case "{":
terminator = "}"
default:
return p.errorf("expected '{' or '<', found %q", tok.value)
}
v := reflect.New(mt.Elem())
if pe := p.readStruct(v.Elem(), terminator); pe != nil {
return pe
}
b, err := Marshal(v.Interface().(Message))
if err != nil {
return p.errorf("failed to marshal message of type %q: %v", messageName, err)
}
if fieldSet["type_url"] {
return p.errorf(anyRepeatedlyUnpacked, "type_url")
}
if fieldSet["value"] {
return p.errorf(anyRepeatedlyUnpacked, "value")
}
sv.FieldByName("TypeUrl").SetString(extName)
sv.FieldByName("Value").SetBytes(b)
fieldSet["type_url"] = true
fieldSet["value"] = true
continue
}
var desc *ExtensionDesc var desc *ExtensionDesc
// This could be faster, but it's functional. // This could be faster, but it's functional.
// TODO: Do something smarter than a linear scan. // TODO: Do something smarter than a linear scan.
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) { for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
if d.Name == tok.value { if d.Name == extName {
desc = d desc = d
break break
} }
} }
if desc == nil { if desc == nil {
return p.errorf("unrecognized extension %q", tok.value) return p.errorf("unrecognized extension %q", extName)
}
// Check the extension terminator.
tok = p.next()
if tok.err != nil {
return tok.err
}
if tok.value != "]" {
return p.errorf("unrecognized extension terminator %q", tok.value)
} }
props := &Properties{} props := &Properties{}
@ -514,7 +561,7 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
} }
reqFieldErr = err reqFieldErr = err
} }
ep := sv.Addr().Interface().(extendableProto) ep := sv.Addr().Interface().(Message)
if !rep { if !rep {
SetExtension(ep, desc, ext.Interface()) SetExtension(ep, desc, ext.Interface())
} else { } else {
@ -545,7 +592,11 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
props = oop.Prop props = oop.Prop
nv := reflect.New(oop.Type.Elem()) nv := reflect.New(oop.Type.Elem())
dst = nv.Elem().Field(0) dst = nv.Elem().Field(0)
sv.Field(oop.Field).Set(nv) field := sv.Field(oop.Field)
if !field.IsNil() {
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
}
field.Set(nv)
} }
if !dst.IsValid() { if !dst.IsValid() {
return p.errorf("unknown field name %q in %v", name, st) return p.errorf("unknown field name %q in %v", name, st)
@ -566,8 +617,9 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
// The map entry should be this sequence of tokens: // The map entry should be this sequence of tokens:
// < key : KEY value : VALUE > // < key : KEY value : VALUE >
// Technically the "key" and "value" could come in any order, // However, implementations may omit key or value, and technically
// but in practice they won't. // we should support them in any order. See b/28924776 for a time
// this went wrong.
tok := p.next() tok := p.next()
var terminator string var terminator string
@ -579,32 +631,39 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
default: default:
return p.errorf("expected '{' or '<', found %q", tok.value) return p.errorf("expected '{' or '<', found %q", tok.value)
} }
if err := p.consumeToken("key"); err != nil { for {
return err tok := p.next()
} if tok.err != nil {
if err := p.consumeToken(":"); err != nil { return tok.err
return err }
} if tok.value == terminator {
if err := p.readAny(key, props.mkeyprop); err != nil { break
return err }
} switch tok.value {
if err := p.consumeOptionalSeparator(); err != nil { case "key":
return err if err := p.consumeToken(":"); err != nil {
} return err
if err := p.consumeToken("value"); err != nil { }
return err if err := p.readAny(key, props.mkeyprop); err != nil {
} return err
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil { }
return err if err := p.consumeOptionalSeparator(); err != nil {
} return err
if err := p.readAny(val, props.mvalprop); err != nil { }
return err case "value":
} if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
if err := p.consumeOptionalSeparator(); err != nil { return err
return err }
} if err := p.readAny(val, props.mvalprop); err != nil {
if err := p.consumeToken(terminator); err != nil { return err
return err }
if err := p.consumeOptionalSeparator(); err != nil {
return err
}
default:
p.back()
return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
}
} }
dst.SetMapIndex(key, val) dst.SetMapIndex(key, val)
@ -627,7 +686,8 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
return err return err
} }
reqFieldErr = err reqFieldErr = err
} else if props.Required { }
if props.Required {
reqCount-- reqCount--
} }
@ -643,6 +703,35 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
return reqFieldErr return reqFieldErr
} }
// consumeExtName consumes extension name or expanded Any type URL and the
// following ']'. It returns the name or URL consumed.
func (p *textParser) consumeExtName() (string, error) {
tok := p.next()
if tok.err != nil {
return "", tok.err
}
// If extension name or type url is quoted, it's a single token.
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
if err != nil {
return "", err
}
return name, p.consumeToken("]")
}
// Consume everything up to "]"
var parts []string
for tok.value != "]" {
parts = append(parts, tok.value)
tok = p.next()
if tok.err != nil {
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
}
}
return strings.Join(parts, ""), nil
}
// consumeOptionalSeparator consumes an optional semicolon or comma. // consumeOptionalSeparator consumes an optional semicolon or comma.
// It is used in readStruct to provide backward compatibility. // It is used in readStruct to provide backward compatibility.
func (p *textParser) consumeOptionalSeparator() error { func (p *textParser) consumeOptionalSeparator() error {
@ -707,12 +796,12 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error {
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
return p.readAny(fv.Index(fv.Len()-1), props) return p.readAny(fv.Index(fv.Len()-1), props)
case reflect.Bool: case reflect.Bool:
// Either "true", "false", 1 or 0. // true/1/t/True or false/f/0/False.
switch tok.value { switch tok.value {
case "true", "1": case "true", "1", "t", "True":
fv.SetBool(true) fv.SetBool(true)
return nil return nil
case "false", "0": case "false", "0", "f", "False":
fv.SetBool(false) fv.SetBool(false)
return nil return nil
} }
@ -776,7 +865,7 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error {
return p.readStruct(fv, terminator) return p.readStruct(fv, terminator)
case reflect.Uint32: case reflect.Uint32:
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
fv.SetUint(uint64(x)) fv.SetUint(x)
return nil return nil
} }
case reflect.Uint64: case reflect.Uint64:

View File

@ -56,25 +56,91 @@ Once you have an instance of the factory created we can create a configuration
struct describing how the container is to be created. A sample would look similar to this: struct describing how the container is to be created. A sample would look similar to this:
```go ```go
defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV defaultMountFlags := unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
config := &configs.Config{ config := &configs.Config{
Rootfs: "/your/path/to/rootfs", Rootfs: "/your/path/to/rootfs",
Capabilities: []string{ Capabilities: &configs.Capabilities{
"CAP_CHOWN", Bounding: []string{
"CAP_DAC_OVERRIDE", "CAP_CHOWN",
"CAP_FSETID", "CAP_DAC_OVERRIDE",
"CAP_FOWNER", "CAP_FSETID",
"CAP_MKNOD", "CAP_FOWNER",
"CAP_NET_RAW", "CAP_MKNOD",
"CAP_SETGID", "CAP_NET_RAW",
"CAP_SETUID", "CAP_SETGID",
"CAP_SETFCAP", "CAP_SETUID",
"CAP_SETPCAP", "CAP_SETFCAP",
"CAP_NET_BIND_SERVICE", "CAP_SETPCAP",
"CAP_SYS_CHROOT", "CAP_NET_BIND_SERVICE",
"CAP_KILL", "CAP_SYS_CHROOT",
"CAP_AUDIT_WRITE", "CAP_KILL",
}, "CAP_AUDIT_WRITE",
},
Effective: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
Inheritable: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
Permitted: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
Ambient: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
},
Namespaces: configs.Namespaces([]configs.Namespace{ Namespaces: configs.Namespaces([]configs.Namespace{
{Type: configs.NEWNS}, {Type: configs.NEWNS},
{Type: configs.NEWUTS}, {Type: configs.NEWUTS},
@ -112,14 +178,14 @@ config := &configs.Config{
Source: "tmpfs", Source: "tmpfs",
Destination: "/dev", Destination: "/dev",
Device: "tmpfs", Device: "tmpfs",
Flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, Flags: unix.MS_NOSUID | unix.MS_STRICTATIME,
Data: "mode=755", Data: "mode=755",
}, },
{ {
Source: "devpts", Source: "devpts",
Destination: "/dev/pts", Destination: "/dev/pts",
Device: "devpts", Device: "devpts",
Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, Flags: unix.MS_NOSUID | unix.MS_NOEXEC,
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5", Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
}, },
{ {
@ -139,7 +205,7 @@ config := &configs.Config{
Source: "sysfs", Source: "sysfs",
Destination: "/sys", Destination: "/sys",
Device: "sysfs", Device: "sysfs",
Flags: defaultMountFlags | syscall.MS_RDONLY, Flags: defaultMountFlags | unix.MS_RDONLY,
}, },
}, },
UidMappings: []configs.IDMap{ UidMappings: []configs.IDMap{
@ -165,7 +231,7 @@ config := &configs.Config{
}, },
Rlimits: []configs.Rlimit{ Rlimits: []configs.Rlimit{
{ {
Type: syscall.RLIMIT_NOFILE, Type: unix.RLIMIT_NOFILE,
Hard: uint64(1025), Hard: uint64(1025),
Soft: uint64(1025), Soft: uint64(1025),
}, },

View File

@ -57,10 +57,11 @@ func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) erro
if dir == "" { if dir == "" {
return nil return nil
} }
root, err := getCgroupRoot() mountInfo, err := ioutil.ReadFile("/proc/self/mountinfo")
if err != nil { if err != nil {
return err return err
} }
root := filepath.Dir(cgroups.GetClosestMountpointAncestor(dir, string(mountInfo)))
// 'ensureParent' start with parent because we don't want to // 'ensureParent' start with parent because we don't want to
// explicitly inherit from parent, it could conflict with // explicitly inherit from parent, it could conflict with
// 'cpuset.cpu_exclusive'. // 'cpuset.cpu_exclusive'.

View File

@ -10,10 +10,12 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall" // only for Errno
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"golang.org/x/sys/unix"
) )
const ( const (
@ -71,14 +73,14 @@ func EnableKernelMemoryAccounting(path string) error {
// until a limit is set on the cgroup and limit cannot be set once the // until a limit is set on the cgroup and limit cannot be set once the
// cgroup has children, or if there are already tasks in the cgroup. // cgroup has children, or if there are already tasks in the cgroup.
for _, i := range []int64{1, -1} { for _, i := range []int64{1, -1} {
if err := setKernelMemory(path, uint64(i)); err != nil { if err := setKernelMemory(path, i); err != nil {
return err return err
} }
} }
return nil return nil
} }
func setKernelMemory(path string, kernelMemoryLimit uint64) error { func setKernelMemory(path string, kernelMemoryLimit int64) error {
if path == "" { if path == "" {
return fmt.Errorf("no such directory for %s", cgroupKernelMemoryLimit) return fmt.Errorf("no such directory for %s", cgroupKernelMemoryLimit)
} }
@ -86,14 +88,14 @@ func setKernelMemory(path string, kernelMemoryLimit uint64) error {
// kernel memory is not enabled on the system so we should do nothing // kernel memory is not enabled on the system so we should do nothing
return nil return nil
} }
if err := ioutil.WriteFile(filepath.Join(path, cgroupKernelMemoryLimit), []byte(strconv.FormatUint(kernelMemoryLimit, 10)), 0700); err != nil { if err := ioutil.WriteFile(filepath.Join(path, cgroupKernelMemoryLimit), []byte(strconv.FormatInt(kernelMemoryLimit, 10)), 0700); err != nil {
// Check if the error number returned by the syscall is "EBUSY" // Check if the error number returned by the syscall is "EBUSY"
// The EBUSY signal is returned on attempts to write to the // The EBUSY signal is returned on attempts to write to the
// memory.kmem.limit_in_bytes file if the cgroup has children or // memory.kmem.limit_in_bytes file if the cgroup has children or
// once tasks have been attached to the cgroup // once tasks have been attached to the cgroup
if pathErr, ok := err.(*os.PathError); ok { if pathErr, ok := err.(*os.PathError); ok {
if errNo, ok := pathErr.Err.(syscall.Errno); ok { if errNo, ok := pathErr.Err.(syscall.Errno); ok {
if errNo == syscall.EBUSY { if errNo == unix.EBUSY {
return fmt.Errorf("failed to set %s, because either tasks have already joined this cgroup or it has children", cgroupKernelMemoryLimit) return fmt.Errorf("failed to set %s, because either tasks have already joined this cgroup or it has children", cgroupKernelMemoryLimit)
} }
} }
@ -104,14 +106,12 @@ func setKernelMemory(path string, kernelMemoryLimit uint64) error {
} }
func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error { func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
ulimited := -1 // If the memory update is set to -1 we should also
// set swap to -1, it means unlimited memory.
// If the memory update is set to uint64(-1) we should also if cgroup.Resources.Memory == -1 {
// set swap to uint64(-1), it means unlimited memory. // Only set swap if it's enabled in kernel
if cgroup.Resources.Memory == uint64(ulimited) {
// Only set swap if it's enbled in kernel
if cgroups.PathExists(filepath.Join(path, cgroupMemorySwapLimit)) { if cgroups.PathExists(filepath.Join(path, cgroupMemorySwapLimit)) {
cgroup.Resources.MemorySwap = uint64(ulimited) cgroup.Resources.MemorySwap = -1
} }
} }
@ -126,29 +126,29 @@ func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
// When update memory limit, we should adapt the write sequence // When update memory limit, we should adapt the write sequence
// for memory and swap memory, so it won't fail because the new // for memory and swap memory, so it won't fail because the new
// value and the old value don't fit kernel's validation. // value and the old value don't fit kernel's validation.
if cgroup.Resources.MemorySwap == uint64(ulimited) || memoryUsage.Limit < cgroup.Resources.MemorySwap { if cgroup.Resources.MemorySwap == -1 || memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) {
if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatUint(cgroup.Resources.MemorySwap, 10)); err != nil { if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
return err return err
} }
if err := writeFile(path, cgroupMemoryLimit, strconv.FormatUint(cgroup.Resources.Memory, 10)); err != nil { if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
return err return err
} }
} else { } else {
if err := writeFile(path, cgroupMemoryLimit, strconv.FormatUint(cgroup.Resources.Memory, 10)); err != nil { if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
return err return err
} }
if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatUint(cgroup.Resources.MemorySwap, 10)); err != nil { if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
return err return err
} }
} }
} else { } else {
if cgroup.Resources.Memory != 0 { if cgroup.Resources.Memory != 0 {
if err := writeFile(path, cgroupMemoryLimit, strconv.FormatUint(cgroup.Resources.Memory, 10)); err != nil { if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
return err return err
} }
} }
if cgroup.Resources.MemorySwap != 0 { if cgroup.Resources.MemorySwap != 0 {
if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatUint(cgroup.Resources.MemorySwap, 10)); err != nil { if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
return err return err
} }
} }
@ -169,13 +169,13 @@ func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error {
} }
if cgroup.Resources.MemoryReservation != 0 { if cgroup.Resources.MemoryReservation != 0 {
if err := writeFile(path, "memory.soft_limit_in_bytes", strconv.FormatUint(cgroup.Resources.MemoryReservation, 10)); err != nil { if err := writeFile(path, "memory.soft_limit_in_bytes", strconv.FormatInt(cgroup.Resources.MemoryReservation, 10)); err != nil {
return err return err
} }
} }
if cgroup.Resources.KernelMemoryTCP != 0 { if cgroup.Resources.KernelMemoryTCP != 0 {
if err := writeFile(path, "memory.kmem.tcp.limit_in_bytes", strconv.FormatUint(cgroup.Resources.KernelMemoryTCP, 10)); err != nil { if err := writeFile(path, "memory.kmem.tcp.limit_in_bytes", strconv.FormatInt(cgroup.Resources.KernelMemoryTCP, 10)); err != nil {
return err return err
} }
} }

View File

@ -260,7 +260,7 @@ func (m *Manager) Apply(pid int) error {
if c.Resources.Memory != 0 { if c.Resources.Memory != 0 {
properties = append(properties, properties = append(properties,
newProp("MemoryLimit", c.Resources.Memory)) newProp("MemoryLimit", uint64(c.Resources.Memory)))
} }
if c.Resources.CpuShares != 0 { if c.Resources.CpuShares != 0 {
@ -353,7 +353,6 @@ func joinCgroups(c *configs.Cgroup, pid int) error {
switch name { switch name {
case "name=systemd": case "name=systemd":
// let systemd handle this // let systemd handle this
break
case "cpuset": case "cpuset":
path, err := getSubsystemPath(c, name) path, err := getSubsystemPath(c, name)
if err != nil && !cgroups.IsNotFound(err) { if err != nil && !cgroups.IsNotFound(err) {
@ -363,7 +362,6 @@ func joinCgroups(c *configs.Cgroup, pid int) error {
if err := s.ApplyDir(path, c, pid); err != nil { if err := s.ApplyDir(path, c, pid); err != nil {
return err return err
} }
break
default: default:
_, err := join(c, name, pid) _, err := join(c, name, pid)
if err != nil { if err != nil {

View File

@ -66,6 +66,21 @@ func isSubsystemAvailable(subsystem string) bool {
return avail return avail
} }
func GetClosestMountpointAncestor(dir, mountinfo string) string {
deepestMountPoint := ""
for _, mountInfoEntry := range strings.Split(mountinfo, "\n") {
mountInfoParts := strings.Fields(mountInfoEntry)
if len(mountInfoParts) < 5 {
continue
}
mountPoint := mountInfoParts[4]
if strings.HasPrefix(mountPoint, deepestMountPoint) && strings.HasPrefix(dir, mountPoint) {
deepestMountPoint = mountPoint
}
}
return deepestMountPoint
}
func FindCgroupMountpointDir() (string, error) { func FindCgroupMountpointDir() (string, error) {
f, err := os.Open("/proc/self/mountinfo") f, err := os.Open("/proc/self/mountinfo")
if err != nil { if err != nil {

View File

@ -1,5 +1,3 @@
// +build linux freebsd
package configs package configs
type FreezerState string type FreezerState string
@ -45,19 +43,19 @@ type Resources struct {
Devices []*Device `json:"devices"` Devices []*Device `json:"devices"`
// Memory limit (in bytes) // Memory limit (in bytes)
Memory uint64 `json:"memory"` Memory int64 `json:"memory"`
// Memory reservation or soft_limit (in bytes) // Memory reservation or soft_limit (in bytes)
MemoryReservation uint64 `json:"memory_reservation"` MemoryReservation int64 `json:"memory_reservation"`
// Total memory usage (memory + swap); set `-1` to enable unlimited swap // Total memory usage (memory + swap); set `-1` to enable unlimited swap
MemorySwap uint64 `json:"memory_swap"` MemorySwap int64 `json:"memory_swap"`
// Kernel memory limit (in bytes) // Kernel memory limit (in bytes)
KernelMemory uint64 `json:"kernel_memory"` KernelMemory int64 `json:"kernel_memory"`
// Kernel memory limit for TCP use (in bytes) // Kernel memory limit for TCP use (in bytes)
KernelMemoryTCP uint64 `json:"kernel_memory_tcp"` KernelMemoryTCP int64 `json:"kernel_memory_tcp"`
// CPU shares (relative weight vs. other containers) // CPU shares (relative weight vs. other containers)
CpuShares uint64 `json:"cpu_shares"` CpuShares uint64 `json:"cpu_shares"`

View File

@ -7,8 +7,9 @@ import (
"os/exec" "os/exec"
"time" "time"
"github.com/Sirupsen/logrus"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
) )
type Rlimit struct { type Rlimit struct {

View File

@ -1,5 +1,3 @@
// +build freebsd linux
package configs package configs
import "fmt" import "fmt"

View File

@ -1,5 +1,3 @@
// +build linux freebsd
package configs package configs
import ( import (
@ -64,12 +62,12 @@ func IsNamespaceSupported(ns NamespaceType) bool {
func NamespaceTypes() []NamespaceType { func NamespaceTypes() []NamespaceType {
return []NamespaceType{ return []NamespaceType{
NEWUSER, // Keep user NS always first, don't move it.
NEWIPC,
NEWUTS,
NEWNET, NEWNET,
NEWPID, NEWPID,
NEWNS, NEWNS,
NEWUTS,
NEWIPC,
NEWUSER,
} }
} }
@ -81,9 +79,6 @@ type Namespace struct {
} }
func (n *Namespace) GetPath(pid int) string { func (n *Namespace) GetPath(pid int) string {
if n.Path != "" {
return n.Path
}
return fmt.Sprintf("/proc/%d/ns/%s", pid, NsName(n.Type)) return fmt.Sprintf("/proc/%d/ns/%s", pid, NsName(n.Type))
} }

View File

@ -2,19 +2,19 @@
package configs package configs
import "syscall" import "golang.org/x/sys/unix"
func (n *Namespace) Syscall() int { func (n *Namespace) Syscall() int {
return namespaceInfo[n.Type] return namespaceInfo[n.Type]
} }
var namespaceInfo = map[NamespaceType]int{ var namespaceInfo = map[NamespaceType]int{
NEWNET: syscall.CLONE_NEWNET, NEWNET: unix.CLONE_NEWNET,
NEWNS: syscall.CLONE_NEWNS, NEWNS: unix.CLONE_NEWNS,
NEWUSER: syscall.CLONE_NEWUSER, NEWUSER: unix.CLONE_NEWUSER,
NEWIPC: syscall.CLONE_NEWIPC, NEWIPC: unix.CLONE_NEWIPC,
NEWUTS: syscall.CLONE_NEWUTS, NEWUTS: unix.CLONE_NEWUTS,
NEWPID: syscall.CLONE_NEWPID, NEWPID: unix.CLONE_NEWPID,
} }
// CloneFlags parses the container's Namespaces options to set the correct // CloneFlags parses the container's Namespaces options to set the correct

View File

@ -1,4 +1,4 @@
// +build !linux,!freebsd // +build !linux
package configs package configs

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/selinux" selinux "github.com/opencontainers/selinux/go-selinux"
) )
type Validator interface { type Validator interface {
@ -92,7 +92,7 @@ func (v *ConfigValidator) security(config *configs.Config) error {
!config.Namespaces.Contains(configs.NEWNS) { !config.Namespaces.Contains(configs.NEWNS) {
return fmt.Errorf("unable to restrict sys entries without a private MNT namespace") return fmt.Errorf("unable to restrict sys entries without a private MNT namespace")
} }
if config.ProcessLabel != "" && !selinux.SelinuxEnabled() { if config.ProcessLabel != "" && !selinux.GetEnabled() {
return fmt.Errorf("selinux label is specified in config, but selinux is disabled or not supported") return fmt.Errorf("selinux label is specified in config, but selinux is disabled or not supported")
} }

View File

@ -3,8 +3,9 @@ package libcontainer
import ( import (
"fmt" "fmt"
"os" "os"
"syscall"
"unsafe" "unsafe"
"golang.org/x/sys/unix"
) )
func ConsoleFromFile(f *os.File) Console { func ConsoleFromFile(f *os.File) Console {
@ -16,13 +17,10 @@ func ConsoleFromFile(f *os.File) Console {
// newConsole returns an initialized console that can be used within a container by copying bytes // newConsole returns an initialized console that can be used within a container by copying bytes
// from the master side to the slave that is attached as the tty for the container's init process. // from the master side to the slave that is attached as the tty for the container's init process.
func newConsole() (Console, error) { func newConsole() (Console, error) {
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0) master, err := os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := saneTerminal(master); err != nil {
return nil, err
}
console, err := ptsname(master) console, err := ptsname(master)
if err != nil { if err != nil {
return nil, err return nil, err
@ -68,8 +66,8 @@ func (c *linuxConsole) Close() error {
// mount initializes the console inside the rootfs mounting with the specified mount label // mount initializes the console inside the rootfs mounting with the specified mount label
// and applying the correct ownership of the console. // and applying the correct ownership of the console.
func (c *linuxConsole) mount() error { func (c *linuxConsole) mount() error {
oldMask := syscall.Umask(0000) oldMask := unix.Umask(0000)
defer syscall.Umask(oldMask) defer unix.Umask(oldMask)
f, err := os.Create("/dev/console") f, err := os.Create("/dev/console")
if err != nil && !os.IsExist(err) { if err != nil && !os.IsExist(err) {
return err return err
@ -77,19 +75,19 @@ func (c *linuxConsole) mount() error {
if f != nil { if f != nil {
f.Close() f.Close()
} }
return syscall.Mount(c.slavePath, "/dev/console", "bind", syscall.MS_BIND, "") return unix.Mount(c.slavePath, "/dev/console", "bind", unix.MS_BIND, "")
} }
// dupStdio opens the slavePath for the console and dups the fds to the current // dupStdio opens the slavePath for the console and dups the fds to the current
// processes stdio, fd 0,1,2. // processes stdio, fd 0,1,2.
func (c *linuxConsole) dupStdio() error { func (c *linuxConsole) dupStdio() error {
slave, err := c.open(syscall.O_RDWR) slave, err := c.open(unix.O_RDWR)
if err != nil { if err != nil {
return err return err
} }
fd := int(slave.Fd()) fd := int(slave.Fd())
for _, i := range []int{0, 1, 2} { for _, i := range []int{0, 1, 2} {
if err := syscall.Dup3(fd, i, 0); err != nil { if err := unix.Dup3(fd, i, 0); err != nil {
return err return err
} }
} }
@ -98,7 +96,7 @@ func (c *linuxConsole) dupStdio() error {
// open is a clone of os.OpenFile without the O_CLOEXEC used to open the pty slave. // open is a clone of os.OpenFile without the O_CLOEXEC used to open the pty slave.
func (c *linuxConsole) open(flag int) (*os.File, error) { func (c *linuxConsole) open(flag int) (*os.File, error) {
r, e := syscall.Open(c.slavePath, flag, 0) r, e := unix.Open(c.slavePath, flag, 0)
if e != nil { if e != nil {
return nil, &os.PathError{ return nil, &os.PathError{
Op: "open", Op: "open",
@ -110,7 +108,7 @@ func (c *linuxConsole) open(flag int) (*os.File, error) {
} }
func ioctl(fd uintptr, flag, data uintptr) error { func ioctl(fd uintptr, flag, data uintptr) error {
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 { if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, flag, data); err != 0 {
return err return err
} }
return nil return nil
@ -120,35 +118,33 @@ func ioctl(fd uintptr, flag, data uintptr) error {
// unlockpt should be called before opening the slave side of a pty. // unlockpt should be called before opening the slave side of a pty.
func unlockpt(f *os.File) error { func unlockpt(f *os.File) error {
var u int32 var u int32
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) return ioctl(f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
} }
// ptsname retrieves the name of the first available pts for the given master. // ptsname retrieves the name of the first available pts for the given master.
func ptsname(f *os.File) (string, error) { func ptsname(f *os.File) (string, error) {
var n int32 n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN)
if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil { if err != nil {
return "", err return "", err
} }
return fmt.Sprintf("/dev/pts/%d", n), nil return fmt.Sprintf("/dev/pts/%d", n), nil
} }
// saneTerminal sets the necessary tty_ioctl(4)s to ensure that a pty pair // SaneTerminal sets the necessary tty_ioctl(4)s to ensure that a pty pair
// created by us acts normally. In particular, a not-very-well-known default of // created by us acts normally. In particular, a not-very-well-known default of
// Linux unix98 ptys is that they have +onlcr by default. While this isn't a // Linux unix98 ptys is that they have +onlcr by default. While this isn't a
// problem for terminal emulators, because we relay data from the terminal we // problem for terminal emulators, because we relay data from the terminal we
// also relay that funky line discipline. // also relay that funky line discipline.
func saneTerminal(terminal *os.File) error { func SaneTerminal(terminal *os.File) error {
// Go doesn't have a wrapper for any of the termios ioctls. termios, err := unix.IoctlGetTermios(int(terminal.Fd()), unix.TCGETS)
var termios syscall.Termios if err != nil {
if err := ioctl(terminal.Fd(), syscall.TCGETS, uintptr(unsafe.Pointer(&termios))); err != nil {
return fmt.Errorf("ioctl(tty, tcgets): %s", err.Error()) return fmt.Errorf("ioctl(tty, tcgets): %s", err.Error())
} }
// Set -onlcr so we don't have to deal with \r. // Set -onlcr so we don't have to deal with \r.
termios.Oflag &^= syscall.ONLCR termios.Oflag &^= unix.ONLCR
if err := ioctl(terminal.Fd(), syscall.TCSETS, uintptr(unsafe.Pointer(&termios))); err != nil { if err := unix.IoctlSetTermios(int(terminal.Fd()), unix.TCSETS, termios); err != nil {
return fmt.Errorf("ioctl(tty, tcsets): %s", err.Error()) return fmt.Errorf("ioctl(tty, tcsets): %s", err.Error())
} }

View File

@ -54,7 +54,7 @@ type BaseState struct {
InitProcessPid int `json:"init_process_pid"` InitProcessPid int `json:"init_process_pid"`
// InitProcessStartTime is the init process start time in clock cycles since boot time. // InitProcessStartTime is the init process start time in clock cycles since boot time.
InitProcessStartTime string `json:"init_process_start"` InitProcessStartTime uint64 `json:"init_process_start"`
// Created is the unix timestamp for the creation time of the container in UTC // Created is the unix timestamp for the creation time of the container in UTC
Created time.Time `json:"created"` Created time.Time `json:"created"`

View File

@ -8,24 +8,27 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"syscall" "syscall" // only for SysProcAttr and Signal
"time" "time"
"github.com/Sirupsen/logrus"
"github.com/golang/protobuf/proto"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/criurpc" "github.com/opencontainers/runc/libcontainer/criurpc"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/runc/libcontainer/utils" "github.com/opencontainers/runc/libcontainer/utils"
"github.com/golang/protobuf/proto"
"github.com/sirupsen/logrus"
"github.com/syndtr/gocapability/capability" "github.com/syndtr/gocapability/capability"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
) )
const stdioFdCount = 3 const stdioFdCount = 3
@ -37,7 +40,7 @@ type linuxContainer struct {
cgroupManager cgroups.Manager cgroupManager cgroups.Manager
initArgs []string initArgs []string
initProcess parentProcess initProcess parentProcess
initProcessStartTime string initProcessStartTime uint64
criuPath string criuPath string
m sync.Mutex m sync.Mutex
criuVersion int criuVersion int
@ -183,8 +186,17 @@ func (c *linuxContainer) Set(config configs.Config) error {
if status == Stopped { if status == Stopped {
return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning) return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
} }
if err := c.cgroupManager.Set(&config); err != nil {
// Set configs back
if err2 := c.cgroupManager.Set(c.config); err2 != nil {
logrus.Warnf("Setting back cgroup configs failed due to error: %v, your state.json and actual configs might be inconsistent.", err2)
}
return err
}
// After config setting succeed, update config and states
c.config = &config c.config = &config
return c.cgroupManager.Set(c.config) _, err = c.updateState(nil)
return err
} }
func (c *linuxContainer) Start(process *Process) error { func (c *linuxContainer) Start(process *Process) error {
@ -263,9 +275,6 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
} }
// generate a timestamp indicating when the container was started // generate a timestamp indicating when the container was started
c.created = time.Now().UTC() c.created = time.Now().UTC()
c.state = &runningState{
c: c,
}
if isInit { if isInit {
c.state = &createdState{ c.state = &createdState{
c: c, c: c,
@ -292,6 +301,10 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
} }
} }
} }
} else {
c.state = &runningState{
c: c,
}
} }
return nil return nil
} }
@ -320,12 +333,12 @@ func (c *linuxContainer) createExecFifo() error {
if _, err := os.Stat(fifoName); err == nil { if _, err := os.Stat(fifoName); err == nil {
return fmt.Errorf("exec fifo %s already exists", fifoName) return fmt.Errorf("exec fifo %s already exists", fifoName)
} }
oldMask := syscall.Umask(0000) oldMask := unix.Umask(0000)
if err := syscall.Mkfifo(fifoName, 0622); err != nil { if err := unix.Mkfifo(fifoName, 0622); err != nil {
syscall.Umask(oldMask) unix.Umask(oldMask)
return err return err
} }
syscall.Umask(oldMask) unix.Umask(oldMask)
if err := os.Chown(fifoName, rootuid, rootgid); err != nil { if err := os.Chown(fifoName, rootuid, rootgid); err != nil {
return err return err
} }
@ -337,6 +350,23 @@ func (c *linuxContainer) deleteExecFifo() {
os.Remove(fifoName) os.Remove(fifoName)
} }
// includeExecFifo opens the container's execfifo as a pathfd, so that the
// container cannot access the statedir (and the FIFO itself remains
// un-opened). It then adds the FifoFd to the given exec.Cmd as an inherited
// fd, with _LIBCONTAINER_FIFOFD set to its fd number.
func (c *linuxContainer) includeExecFifo(cmd *exec.Cmd) error {
fifoName := filepath.Join(c.root, execFifoFilename)
fifoFd, err := unix.Open(fifoName, unix.O_PATH|unix.O_CLOEXEC, 0)
if err != nil {
return err
}
cmd.ExtraFiles = append(cmd.ExtraFiles, os.NewFile(uintptr(fifoFd), fifoName))
cmd.Env = append(cmd.Env,
fmt.Sprintf("_LIBCONTAINER_FIFOFD=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
return nil
}
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) { func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
parentPipe, childPipe, err := utils.NewSockPair("init") parentPipe, childPipe, err := utils.NewSockPair("init")
if err != nil { if err != nil {
@ -350,18 +380,15 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces
return c.newSetnsProcess(p, cmd, parentPipe, childPipe) return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
} }
// We only set up rootDir if we're not doing a `runc exec`. The reason for // We only set up fifoFd if we're not doing a `runc exec`. The historic
// this is to avoid cases where a racing, unprivileged process inside the // reason for this is that previously we would pass a dirfd that allowed
// container can get access to the statedir file descriptor (which would // for container rootfs escape (and not doing it in `runc exec` avoided
// allow for container rootfs escape). // that problem), but we no longer do that. However, there's no need to do
rootDir, err := os.Open(c.root) // this for `runc exec` so we just keep it this way to be safe.
if err != nil { if err := c.includeExecFifo(cmd); err != nil {
return nil, err return nil, newSystemErrorWithCause(err, "including execfifo in cmd.Exec setup")
} }
cmd.ExtraFiles = append(cmd.ExtraFiles, rootDir) return c.newInitProcess(p, cmd, parentPipe, childPipe)
cmd.Env = append(cmd.Env,
fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
return c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir)
} }
func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) { func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) {
@ -393,7 +420,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
return cmd, nil return cmd, nil
} }
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe, rootDir *os.File) (*initProcess, error) { func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) {
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard)) cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard))
nsMaps := make(map[configs.NamespaceType]string) nsMaps := make(map[configs.NamespaceType]string)
for _, ns := range c.config.Namespaces { for _, ns := range c.config.Namespaces {
@ -416,7 +443,6 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
process: p, process: p,
bootstrapData: data, bootstrapData: data,
sharePidns: sharePidns, sharePidns: sharePidns,
rootDir: rootDir,
}, nil }, nil
} }
@ -535,19 +561,63 @@ func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struc
return notifyMemoryPressure(c.cgroupManager.GetPaths(), level) return notifyMemoryPressure(c.cgroupManager.GetPaths(), level)
} }
// checkCriuVersion checks Criu version greater than or equal to minVersion var criuFeatures *criurpc.CriuFeatures
func (c *linuxContainer) checkCriuVersion(minVersion string) error {
var x, y, z, versionReq int
_, err := fmt.Sscanf(minVersion, "%d.%d.%d\n", &x, &y, &z) // 1.5.2 func (c *linuxContainer) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.CriuOpts, criuFeat *criurpc.CriuFeatures) error {
if err != nil {
_, err = fmt.Sscanf(minVersion, "Version: %d.%d\n", &x, &y) // 1.6 var t criurpc.CriuReqType
t = criurpc.CriuReqType_FEATURE_CHECK
// criu 1.8 => 10800
if err := c.checkCriuVersion(10800); err != nil {
// Feature checking was introduced with CRIU 1.8.
// Ignore the feature check if an older CRIU version is used
// and just act as before.
// As all automated PR testing is done using CRIU 1.7 this
// code will not be tested by automated PR testing.
return nil
} }
versionReq = x*10000 + y*100 + z
out, err := exec.Command(c.criuPath, "-V").Output() // make sure the features we are looking for are really not from
// some previous check
criuFeatures = nil
req := &criurpc.CriuReq{
Type: &t,
// Theoretically this should not be necessary but CRIU
// segfaults if Opts is empty.
// Fixed in CRIU 2.12
Opts: rpcOpts,
Features: criuFeat,
}
err := c.criuSwrk(nil, req, criuOpts, false)
if err != nil { if err != nil {
return fmt.Errorf("Unable to execute CRIU command: %s", c.criuPath) logrus.Debugf("%s", err)
return fmt.Errorf("CRIU feature check failed")
}
logrus.Debugf("Feature check says: %s", criuFeatures)
missingFeatures := false
if *criuFeat.MemTrack && !*criuFeatures.MemTrack {
missingFeatures = true
logrus.Debugf("CRIU does not support MemTrack")
}
if missingFeatures {
return fmt.Errorf("CRIU is missing features")
}
return nil
}
func parseCriuVersion(path string) (int, error) {
var x, y, z int
out, err := exec.Command(path, "-V").Output()
if err != nil {
return 0, fmt.Errorf("Unable to execute CRIU command: %s", path)
} }
x = 0 x = 0
@ -559,7 +629,7 @@ func (c *linuxContainer) checkCriuVersion(minVersion string) error {
if sp := strings.Index(string(out), "GitID"); sp > 0 { if sp := strings.Index(string(out), "GitID"); sp > 0 {
version = string(out)[sp:ep] version = string(out)[sp:ep]
} else { } else {
return fmt.Errorf("Unable to parse the CRIU version: %s", c.criuPath) return 0, fmt.Errorf("Unable to parse the CRIU version: %s", path)
} }
n, err := fmt.Sscanf(string(version), "GitID: v%d.%d.%d", &x, &y, &z) // 1.5.2 n, err := fmt.Sscanf(string(version), "GitID: v%d.%d.%d", &x, &y, &z) // 1.5.2
@ -570,7 +640,7 @@ func (c *linuxContainer) checkCriuVersion(minVersion string) error {
z++ z++
} }
if n < 2 || err != nil { if n < 2 || err != nil {
return fmt.Errorf("Unable to parse the CRIU version: %s %d %s", version, n, err) return 0, fmt.Errorf("Unable to parse the CRIU version: %s %d %s", version, n, err)
} }
} else { } else {
// criu release version format // criu release version format
@ -579,19 +649,81 @@ func (c *linuxContainer) checkCriuVersion(minVersion string) error {
n, err = fmt.Sscanf(string(out), "Version: %d.%d\n", &x, &y) // 1.6 n, err = fmt.Sscanf(string(out), "Version: %d.%d\n", &x, &y) // 1.6
} }
if n < 2 || err != nil { if n < 2 || err != nil {
return fmt.Errorf("Unable to parse the CRIU version: %s %d %s", out, n, err) return 0, fmt.Errorf("Unable to parse the CRIU version: %s %d %s", out, n, err)
} }
} }
c.criuVersion = x*10000 + y*100 + z return x*10000 + y*100 + z, nil
}
if c.criuVersion < versionReq { func compareCriuVersion(criuVersion int, minVersion int) error {
return fmt.Errorf("CRIU version must be %s or higher", minVersion) // simple function to perform the actual version compare
if criuVersion < minVersion {
return fmt.Errorf("CRIU version %d must be %d or higher", criuVersion, minVersion)
} }
return nil return nil
} }
// This is used to store the result of criu version RPC
var criuVersionRPC *criurpc.CriuVersion
// checkCriuVersion checks Criu version greater than or equal to minVersion
func (c *linuxContainer) checkCriuVersion(minVersion int) error {
// If the version of criu has already been determined there is no need
// to ask criu for the version again. Use the value from c.criuVersion.
if c.criuVersion != 0 {
return compareCriuVersion(c.criuVersion, minVersion)
}
// First try if this version of CRIU support the version RPC.
// The CRIU version RPC was introduced with CRIU 3.0.
// First, reset the variable for the RPC answer to nil
criuVersionRPC = nil
var t criurpc.CriuReqType
t = criurpc.CriuReqType_VERSION
req := &criurpc.CriuReq{
Type: &t,
}
err := c.criuSwrk(nil, req, nil, false)
if err != nil {
return fmt.Errorf("CRIU version check failed: %s", err)
}
if criuVersionRPC != nil {
logrus.Debugf("CRIU version: %s", criuVersionRPC)
// major and minor are always set
c.criuVersion = int(*criuVersionRPC.Major) * 10000
c.criuVersion += int(*criuVersionRPC.Minor) * 100
if criuVersionRPC.Sublevel != nil {
c.criuVersion += int(*criuVersionRPC.Sublevel)
}
if criuVersionRPC.Gitid != nil {
// runc's convention is that a CRIU git release is
// always the same as increasing the minor by 1
c.criuVersion -= (c.criuVersion % 100)
c.criuVersion += 100
}
return compareCriuVersion(c.criuVersion, minVersion)
}
// This is CRIU without the version RPC and therefore
// older than 3.0. Parsing the output is required.
// This can be remove once runc does not work with criu older than 3.0
c.criuVersion, err = parseCriuVersion(c.criuPath)
if err != nil {
return err
}
return compareCriuVersion(c.criuVersion, minVersion)
}
const descriptorsFilename = "descriptors.json" const descriptorsFilename = "descriptors.json"
func (c *linuxContainer) addCriuDumpMount(req *criurpc.CriuReq, m *configs.Mount) { func (c *linuxContainer) addCriuDumpMount(req *criurpc.CriuReq, m *configs.Mount) {
@ -641,7 +773,8 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
return fmt.Errorf("cannot checkpoint a rootless container") return fmt.Errorf("cannot checkpoint a rootless container")
} }
if err := c.checkCriuVersion("1.5.2"); err != nil { // criu 1.5.2 => 10502
if err := c.checkCriuVersion(10502); err != nil {
return err return err
} }
@ -676,20 +809,27 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
defer imageDir.Close() defer imageDir.Close()
rpcOpts := criurpc.CriuOpts{ rpcOpts := criurpc.CriuOpts{
ImagesDirFd: proto.Int32(int32(imageDir.Fd())), ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
WorkDirFd: proto.Int32(int32(workDir.Fd())), WorkDirFd: proto.Int32(int32(workDir.Fd())),
LogLevel: proto.Int32(4), LogLevel: proto.Int32(4),
LogFile: proto.String("dump.log"), LogFile: proto.String("dump.log"),
Root: proto.String(c.config.Rootfs), Root: proto.String(c.config.Rootfs),
ManageCgroups: proto.Bool(true), ManageCgroups: proto.Bool(true),
NotifyScripts: proto.Bool(true), NotifyScripts: proto.Bool(true),
Pid: proto.Int32(int32(c.initProcess.pid())), Pid: proto.Int32(int32(c.initProcess.pid())),
ShellJob: proto.Bool(criuOpts.ShellJob), ShellJob: proto.Bool(criuOpts.ShellJob),
LeaveRunning: proto.Bool(criuOpts.LeaveRunning), LeaveRunning: proto.Bool(criuOpts.LeaveRunning),
TcpEstablished: proto.Bool(criuOpts.TcpEstablished), TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections), ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
FileLocks: proto.Bool(criuOpts.FileLocks), FileLocks: proto.Bool(criuOpts.FileLocks),
EmptyNs: proto.Uint32(criuOpts.EmptyNs), EmptyNs: proto.Uint32(criuOpts.EmptyNs),
OrphanPtsMaster: proto.Bool(true),
AutoDedup: proto.Bool(criuOpts.AutoDedup),
}
fcg := c.cgroupManager.GetPaths()["freezer"]
if fcg != "" {
rpcOpts.FreezeCgroup = proto.String(fcg)
} }
// append optional criu opts, e.g., page-server and port // append optional criu opts, e.g., page-server and port
@ -708,7 +848,8 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
// append optional manage cgroups mode // append optional manage cgroups mode
if criuOpts.ManageCgroupsMode != 0 { if criuOpts.ManageCgroupsMode != 0 {
if err := c.checkCriuVersion("1.7"); err != nil { // criu 1.7 => 10700
if err := c.checkCriuVersion(10700); err != nil {
return err return err
} }
mode := criurpc.CriuCgMode(criuOpts.ManageCgroupsMode) mode := criurpc.CriuCgMode(criuOpts.ManageCgroupsMode)
@ -717,6 +858,14 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
var t criurpc.CriuReqType var t criurpc.CriuReqType
if criuOpts.PreDump { if criuOpts.PreDump {
feat := criurpc.CriuFeatures{
MemTrack: proto.Bool(true),
}
if err := c.checkCriuFeatures(criuOpts, &rpcOpts, &feat); err != nil {
return err
}
t = criurpc.CriuReqType_PRE_DUMP t = criurpc.CriuReqType_PRE_DUMP
} else { } else {
t = criurpc.CriuReqType_DUMP t = criurpc.CriuReqType_DUMP
@ -732,7 +881,6 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
switch m.Device { switch m.Device {
case "bind": case "bind":
c.addCriuDumpMount(req, m) c.addCriuDumpMount(req, m)
break
case "cgroup": case "cgroup":
binds, err := getCgroupMounts(m) binds, err := getCgroupMounts(m)
if err != nil { if err != nil {
@ -741,7 +889,6 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
for _, b := range binds { for _, b := range binds {
c.addCriuDumpMount(req, b) c.addCriuDumpMount(req, b)
} }
break
} }
} }
@ -794,9 +941,8 @@ func (c *linuxContainer) restoreNetwork(req *criurpc.CriuReq, criuOpts *CriuOpts
veth.IfOut = proto.String(iface.HostInterfaceName) veth.IfOut = proto.String(iface.HostInterfaceName)
veth.IfIn = proto.String(iface.Name) veth.IfIn = proto.String(iface.Name)
req.Opts.Veths = append(req.Opts.Veths, veth) req.Opts.Veths = append(req.Opts.Veths, veth)
break
case "loopback": case "loopback":
break // Do nothing
} }
} }
for _, i := range criuOpts.VethPairs { for _, i := range criuOpts.VethPairs {
@ -817,7 +963,8 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
return fmt.Errorf("cannot restore a rootless container") return fmt.Errorf("cannot restore a rootless container")
} }
if err := c.checkCriuVersion("1.5.2"); err != nil { // criu 1.5.2 => 10502
if err := c.checkCriuVersion(10502); err != nil {
return err return err
} }
if criuOpts.WorkDirectory == "" { if criuOpts.WorkDirectory == "" {
@ -855,29 +1002,31 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
if err != nil { if err != nil {
return err return err
} }
err = syscall.Mount(c.config.Rootfs, root, "", syscall.MS_BIND|syscall.MS_REC, "") err = unix.Mount(c.config.Rootfs, root, "", unix.MS_BIND|unix.MS_REC, "")
if err != nil { if err != nil {
return err return err
} }
defer syscall.Unmount(root, syscall.MNT_DETACH) defer unix.Unmount(root, unix.MNT_DETACH)
t := criurpc.CriuReqType_RESTORE t := criurpc.CriuReqType_RESTORE
req := &criurpc.CriuReq{ req := &criurpc.CriuReq{
Type: &t, Type: &t,
Opts: &criurpc.CriuOpts{ Opts: &criurpc.CriuOpts{
ImagesDirFd: proto.Int32(int32(imageDir.Fd())), ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
WorkDirFd: proto.Int32(int32(workDir.Fd())), WorkDirFd: proto.Int32(int32(workDir.Fd())),
EvasiveDevices: proto.Bool(true), EvasiveDevices: proto.Bool(true),
LogLevel: proto.Int32(4), LogLevel: proto.Int32(4),
LogFile: proto.String("restore.log"), LogFile: proto.String("restore.log"),
RstSibling: proto.Bool(true), RstSibling: proto.Bool(true),
Root: proto.String(root), Root: proto.String(root),
ManageCgroups: proto.Bool(true), ManageCgroups: proto.Bool(true),
NotifyScripts: proto.Bool(true), NotifyScripts: proto.Bool(true),
ShellJob: proto.Bool(criuOpts.ShellJob), ShellJob: proto.Bool(criuOpts.ShellJob),
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections), ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
TcpEstablished: proto.Bool(criuOpts.TcpEstablished), TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
FileLocks: proto.Bool(criuOpts.FileLocks), FileLocks: proto.Bool(criuOpts.FileLocks),
EmptyNs: proto.Uint32(criuOpts.EmptyNs), EmptyNs: proto.Uint32(criuOpts.EmptyNs),
OrphanPtsMaster: proto.Bool(true),
AutoDedup: proto.Bool(criuOpts.AutoDedup),
}, },
} }
@ -885,7 +1034,6 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
switch m.Device { switch m.Device {
case "bind": case "bind":
c.addCriuRestoreMount(req, m) c.addCriuRestoreMount(req, m)
break
case "cgroup": case "cgroup":
binds, err := getCgroupMounts(m) binds, err := getCgroupMounts(m)
if err != nil { if err != nil {
@ -894,7 +1042,6 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
for _, b := range binds { for _, b := range binds {
c.addCriuRestoreMount(req, b) c.addCriuRestoreMount(req, b)
} }
break
} }
} }
@ -908,13 +1055,14 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
c.addCriuRestoreMount(req, m) c.addCriuRestoreMount(req, m)
} }
if criuOpts.EmptyNs&syscall.CLONE_NEWNET == 0 { if criuOpts.EmptyNs&unix.CLONE_NEWNET == 0 {
c.restoreNetwork(req, criuOpts) c.restoreNetwork(req, criuOpts)
} }
// append optional manage cgroups mode // append optional manage cgroups mode
if criuOpts.ManageCgroupsMode != 0 { if criuOpts.ManageCgroupsMode != 0 {
if err := c.checkCriuVersion("1.7"); err != nil { // criu 1.7 => 10700
if err := c.checkCriuVersion(10700); err != nil {
return err return err
} }
mode := criurpc.CriuCgMode(criuOpts.ManageCgroupsMode) mode := criurpc.CriuCgMode(criuOpts.ManageCgroupsMode)
@ -949,6 +1097,10 @@ func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
return err return err
} }
if err := c.cgroupManager.Set(c.config); err != nil {
return newSystemError(err)
}
path := fmt.Sprintf("/proc/%d/cgroup", pid) path := fmt.Sprintf("/proc/%d/cgroup", pid)
cgroupsPaths, err := cgroups.ParseCgroupFile(path) cgroupsPaths, err := cgroups.ParseCgroupFile(path)
if err != nil { if err != nil {
@ -967,19 +1119,38 @@ func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
} }
func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *CriuOpts, applyCgroups bool) error { func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *CriuOpts, applyCgroups bool) error {
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET|syscall.SOCK_CLOEXEC, 0) fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0)
if err != nil { if err != nil {
return err return err
} }
logPath := filepath.Join(opts.WorkDirectory, req.GetOpts().GetLogFile()) var logPath string
if opts != nil {
logPath = filepath.Join(opts.WorkDirectory, req.GetOpts().GetLogFile())
} else {
// For the VERSION RPC 'opts' is set to 'nil' and therefore
// opts.WorkDirectory does not exist. Set logPath to "".
logPath = ""
}
criuClient := os.NewFile(uintptr(fds[0]), "criu-transport-client") criuClient := os.NewFile(uintptr(fds[0]), "criu-transport-client")
criuClientFileCon, err := net.FileConn(criuClient)
criuClient.Close()
if err != nil {
return err
}
criuClientCon := criuClientFileCon.(*net.UnixConn)
defer criuClientCon.Close()
criuServer := os.NewFile(uintptr(fds[1]), "criu-transport-server") criuServer := os.NewFile(uintptr(fds[1]), "criu-transport-server")
defer criuClient.Close()
defer criuServer.Close() defer criuServer.Close()
args := []string{"swrk", "3"} args := []string{"swrk", "3"}
logrus.Debugf("Using CRIU %d at: %s", c.criuVersion, c.criuPath) if c.criuVersion != 0 {
// If the CRIU Version is still '0' then this is probably
// the initial CRIU run to detect the version. Skip it.
logrus.Debugf("Using CRIU %d at: %s", c.criuVersion, c.criuPath)
}
logrus.Debugf("Using CRIU with following args: %s", args) logrus.Debugf("Using CRIU with following args: %s", args)
cmd := exec.Command(c.criuPath, args...) cmd := exec.Command(c.criuPath, args...)
if process != nil { if process != nil {
@ -995,7 +1166,7 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
criuServer.Close() criuServer.Close()
defer func() { defer func() {
criuClient.Close() criuClientCon.Close()
_, err := cmd.Process.Wait() _, err := cmd.Process.Wait()
if err != nil { if err != nil {
return return
@ -1018,29 +1189,38 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
} }
logrus.Debugf("Using CRIU in %s mode", req.GetType().String()) logrus.Debugf("Using CRIU in %s mode", req.GetType().String())
val := reflect.ValueOf(req.GetOpts()) // In the case of criurpc.CriuReqType_FEATURE_CHECK req.GetOpts()
v := reflect.Indirect(val) // should be empty. For older CRIU versions it still will be
for i := 0; i < v.NumField(); i++ { // available but empty. criurpc.CriuReqType_VERSION actually
st := v.Type() // has no req.GetOpts().
name := st.Field(i).Name if !(req.GetType() == criurpc.CriuReqType_FEATURE_CHECK ||
if strings.HasPrefix(name, "XXX_") { req.GetType() == criurpc.CriuReqType_VERSION) {
continue
val := reflect.ValueOf(req.GetOpts())
v := reflect.Indirect(val)
for i := 0; i < v.NumField(); i++ {
st := v.Type()
name := st.Field(i).Name
if strings.HasPrefix(name, "XXX_") {
continue
}
value := val.MethodByName("Get" + name).Call([]reflect.Value{})
logrus.Debugf("CRIU option %s with value %v", name, value[0])
} }
value := val.MethodByName("Get" + name).Call([]reflect.Value{})
logrus.Debugf("CRIU option %s with value %v", name, value[0])
} }
data, err := proto.Marshal(req) data, err := proto.Marshal(req)
if err != nil { if err != nil {
return err return err
} }
_, err = criuClient.Write(data) _, err = criuClientCon.Write(data)
if err != nil { if err != nil {
return err return err
} }
buf := make([]byte, 10*4096) buf := make([]byte, 10*4096)
oob := make([]byte, 4096)
for true { for true {
n, err := criuClient.Read(buf) n, oobn, _, _, err := criuClientCon.ReadMsgUnix(buf, oob)
if err != nil { if err != nil {
return err return err
} }
@ -1058,13 +1238,25 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
} }
if !resp.GetSuccess() { if !resp.GetSuccess() {
typeString := req.GetType().String() typeString := req.GetType().String()
if typeString == "VERSION" {
// If the VERSION RPC fails this probably means that the CRIU
// version is too old for this RPC. Just return 'nil'.
return nil
}
return fmt.Errorf("criu failed: type %s errno %d\nlog file: %s", typeString, resp.GetCrErrno(), logPath) return fmt.Errorf("criu failed: type %s errno %d\nlog file: %s", typeString, resp.GetCrErrno(), logPath)
} }
t := resp.GetType() t := resp.GetType()
switch { switch {
case t == criurpc.CriuReqType_VERSION:
logrus.Debugf("CRIU version: %s", resp)
criuVersionRPC = resp.GetVersion()
break
case t == criurpc.CriuReqType_FEATURE_CHECK:
logrus.Debugf("Feature check says: %s", resp)
criuFeatures = resp.GetFeatures()
case t == criurpc.CriuReqType_NOTIFY: case t == criurpc.CriuReqType_NOTIFY:
if err := c.criuNotifications(resp, process, opts, extFds); err != nil { if err := c.criuNotifications(resp, process, opts, extFds, oob[:oobn]); err != nil {
return err return err
} }
t = criurpc.CriuReqType_NOTIFY t = criurpc.CriuReqType_NOTIFY
@ -1076,31 +1268,14 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
if err != nil { if err != nil {
return err return err
} }
_, err = criuClient.Write(data) _, err = criuClientCon.Write(data)
if err != nil { if err != nil {
return err return err
} }
continue continue
case t == criurpc.CriuReqType_RESTORE: case t == criurpc.CriuReqType_RESTORE:
case t == criurpc.CriuReqType_DUMP: case t == criurpc.CriuReqType_DUMP:
break
case t == criurpc.CriuReqType_PRE_DUMP: case t == criurpc.CriuReqType_PRE_DUMP:
// In pre-dump mode CRIU is in a loop and waits for
// the final DUMP command.
// The current runc pre-dump approach, however, is
// start criu in PRE_DUMP once for a single pre-dump
// and not the whole series of pre-dump, pre-dump, ...m, dump
// If we got the message CriuReqType_PRE_DUMP it means
// CRIU was successful and we need to forcefully stop CRIU
logrus.Debugf("PRE_DUMP finished. Send close signal to CRIU service")
criuClient.Close()
// Process status won't be success, because one end of sockets is closed
_, err := cmd.Process.Wait()
if err != nil {
logrus.Debugf("After PRE_DUMP CRIU exiting failed")
return err
}
return nil
default: default:
return fmt.Errorf("unable to parse the response %s", resp.String()) return fmt.Errorf("unable to parse the response %s", resp.String())
} }
@ -1108,13 +1283,22 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
break break
} }
criuClientCon.CloseWrite()
// cmd.Wait() waits cmd.goroutines which are used for proxying file descriptors. // cmd.Wait() waits cmd.goroutines which are used for proxying file descriptors.
// Here we want to wait only the CRIU process. // Here we want to wait only the CRIU process.
st, err := cmd.Process.Wait() st, err := cmd.Process.Wait()
if err != nil { if err != nil {
return err return err
} }
if !st.Success() {
// In pre-dump mode CRIU is in a loop and waits for
// the final DUMP command.
// The current runc pre-dump approach, however, is
// start criu in PRE_DUMP once for a single pre-dump
// and not the whole series of pre-dump, pre-dump, ...m, dump
// If we got the message CriuReqType_PRE_DUMP it means
// CRIU was successful and we need to forcefully stop CRIU
if !st.Success() && *req.Type != criurpc.CriuReqType_PRE_DUMP {
return fmt.Errorf("criu failed: %s\nlog file: %s", st.String(), logPath) return fmt.Errorf("criu failed: %s\nlog file: %s", st.String(), logPath)
} }
return nil return nil
@ -1148,11 +1332,12 @@ func unlockNetwork(config *configs.Config) error {
return nil return nil
} }
func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, opts *CriuOpts, fds []string) error { func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, opts *CriuOpts, fds []string, oob []byte) error {
notify := resp.GetNotify() notify := resp.GetNotify()
if notify == nil { if notify == nil {
return fmt.Errorf("invalid response: %s", resp.String()) return fmt.Errorf("invalid response: %s", resp.String())
} }
logrus.Debugf("notify: %s\n", notify.GetScript())
switch { switch {
case notify.GetScript() == "post-dump": case notify.GetScript() == "post-dump":
f, err := os.Create(filepath.Join(c.root, "checkpoint")) f, err := os.Create(filepath.Join(c.root, "checkpoint"))
@ -1205,12 +1390,31 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
logrus.Error(err) logrus.Error(err)
} }
} }
case notify.GetScript() == "orphan-pts-master":
scm, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return err
}
fds, err := unix.ParseUnixRights(&scm[0])
if err != nil {
return err
}
master := os.NewFile(uintptr(fds[0]), "orphan-pts-master")
defer master.Close()
// While we can access console.master, using the API is a good idea.
if err := utils.SendFd(process.ConsoleSocket, master); err != nil {
return err
}
} }
return nil return nil
} }
func (c *linuxContainer) updateState(process parentProcess) (*State, error) { func (c *linuxContainer) updateState(process parentProcess) (*State, error) {
c.initProcess = process if process != nil {
c.initProcess = process
}
state, err := c.currentState() state, err := c.currentState()
if err != nil { if err != nil {
return nil, err return nil, err
@ -1267,40 +1471,17 @@ func (c *linuxContainer) refreshState() error {
return c.state.transition(&stoppedState{c: c}) return c.state.transition(&stoppedState{c: c})
} }
// doesInitProcessExist checks if the init process is still the same process
// as the initial one, it could happen that the original process has exited
// and a new process has been created with the same pid, in this case, the
// container would already be stopped.
func (c *linuxContainer) doesInitProcessExist(initPid int) (bool, error) {
startTime, err := system.GetProcessStartTime(initPid)
if err != nil {
return false, newSystemErrorWithCausef(err, "getting init process %d start time", initPid)
}
if c.initProcessStartTime != startTime {
return false, nil
}
return true, nil
}
func (c *linuxContainer) runType() (Status, error) { func (c *linuxContainer) runType() (Status, error) {
if c.initProcess == nil { if c.initProcess == nil {
return Stopped, nil return Stopped, nil
} }
pid := c.initProcess.pid() pid := c.initProcess.pid()
// return Running if the init process is alive stat, err := system.Stat(pid)
if err := syscall.Kill(pid, 0); err != nil { if err != nil {
if err == syscall.ESRCH { return Stopped, nil
// It means the process does not exist anymore, could happen when the
// process exited just when we call the function, we should not return
// error in this case.
return Stopped, nil
}
return Stopped, newSystemErrorWithCausef(err, "sending signal 0 to pid %d", pid)
} }
// check if the process is still the original init process. if stat.StartTime != c.initProcessStartTime || stat.State == system.Zombie || stat.State == system.Dead {
exist, err := c.doesInitProcessExist(pid) return Stopped, nil
if !exist || err != nil {
return Stopped, err
} }
// We'll create exec fifo and blocking on it after container is created, // We'll create exec fifo and blocking on it after container is created,
// and delete it after start container. // and delete it after start container.
@ -1311,7 +1492,12 @@ func (c *linuxContainer) runType() (Status, error) {
} }
func (c *linuxContainer) isPaused() (bool, error) { func (c *linuxContainer) isPaused() (bool, error) {
data, err := ioutil.ReadFile(filepath.Join(c.cgroupManager.GetPaths()["freezer"], "freezer.state")) fcg := c.cgroupManager.GetPaths()["freezer"]
if fcg == "" {
// A container doesn't have a freezer cgroup
return false, nil
}
data, err := ioutil.ReadFile(filepath.Join(fcg, "freezer.state"))
if err != nil { if err != nil {
// If freezer cgroup is not mounted, the container would just be not paused. // If freezer cgroup is not mounted, the container would just be not paused.
if os.IsNotExist(err) { if os.IsNotExist(err) {
@ -1324,7 +1510,7 @@ func (c *linuxContainer) isPaused() (bool, error) {
func (c *linuxContainer) currentState() (*State, error) { func (c *linuxContainer) currentState() (*State, error) {
var ( var (
startTime string startTime uint64
externalDescriptors []string externalDescriptors []string
pid = -1 pid = -1
) )
@ -1367,28 +1553,18 @@ func (c *linuxContainer) currentState() (*State, error) {
// can setns in order. // can setns in order.
func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceType]string) ([]string, error) { func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceType]string) ([]string, error) {
paths := []string{} paths := []string{}
order := []configs.NamespaceType{
// The user namespace *must* be done first.
configs.NEWUSER,
configs.NEWIPC,
configs.NEWUTS,
configs.NEWNET,
configs.NEWPID,
configs.NEWNS,
}
// Remove namespaces that we don't need to join. for _, ns := range configs.NamespaceTypes() {
var nsTypes []configs.NamespaceType
for _, ns := range order { // Remove namespaces that we don't need to join.
if c.config.Namespaces.Contains(ns) { if !c.config.Namespaces.Contains(ns) {
nsTypes = append(nsTypes, ns) continue
} }
}
for _, nsType := range nsTypes { if p, ok := namespaces[ns]; ok && p != "" {
if p, ok := namespaces[nsType]; ok && p != "" {
// check if the requested namespace is supported // check if the requested namespace is supported
if !configs.IsNamespaceSupported(nsType) { if !configs.IsNamespaceSupported(ns) {
return nil, newSystemError(fmt.Errorf("namespace %s is not supported", nsType)) return nil, newSystemError(fmt.Errorf("namespace %s is not supported", ns))
} }
// only set to join this namespace if it exists // only set to join this namespace if it exists
if _, err := os.Lstat(p); err != nil { if _, err := os.Lstat(p); err != nil {
@ -1399,9 +1575,11 @@ func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceTyp
if strings.ContainsRune(p, ',') { if strings.ContainsRune(p, ',') {
return nil, newSystemError(fmt.Errorf("invalid path %s", p)) return nil, newSystemError(fmt.Errorf("invalid path %s", p))
} }
paths = append(paths, fmt.Sprintf("%s:%s", configs.NsName(nsType), p)) paths = append(paths, fmt.Sprintf("%s:%s", configs.NsName(ns), p))
} }
} }
return paths, nil return paths, nil
} }

View File

@ -1,5 +1,3 @@
// +build linux freebsd
package libcontainer package libcontainer
// cgroup restoring strategy provided by criu // cgroup restoring strategy provided by criu
@ -36,4 +34,5 @@ type CriuOpts struct {
VethPairs []VethPairName // pass the veth to criu when restore VethPairs []VethPairName // pass the veth to criu when restore
ManageCgroupsMode cgMode // dump or restore cgroup mode ManageCgroupsMode cgMode // dump or restore cgroup mode
EmptyNs uint32 // don't c/r properties for namespace from this mask EmptyNs uint32 // don't c/r properties for namespace from this mask
AutoDedup bool // auto deduplication for incremental dumps
} }

View File

@ -12,6 +12,7 @@ It has these top-level messages:
CriuPageServerInfo CriuPageServerInfo
CriuVethPair CriuVethPair
ExtMountMap ExtMountMap
JoinNamespace
InheritFd InheritFd
CgroupRoot CgroupRoot
UnixSk UnixSk
@ -22,21 +23,30 @@ It has these top-level messages:
CriuFeatures CriuFeatures
CriuReq CriuReq
CriuResp CriuResp
CriuVersion
*/ */
package criurpc package criurpc
import proto "github.com/golang/protobuf/proto" import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math" import math "math"
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type CriuCgMode int32 type CriuCgMode int32
const ( const (
CriuCgMode_IGNORE CriuCgMode = 0 CriuCgMode_IGNORE CriuCgMode = 0
CriuCgMode_NONE CriuCgMode = 1 CriuCgMode_CG_NONE CriuCgMode = 1
CriuCgMode_PROPS CriuCgMode = 2 CriuCgMode_PROPS CriuCgMode = 2
CriuCgMode_SOFT CriuCgMode = 3 CriuCgMode_SOFT CriuCgMode = 3
CriuCgMode_FULL CriuCgMode = 4 CriuCgMode_FULL CriuCgMode = 4
@ -46,7 +56,7 @@ const (
var CriuCgMode_name = map[int32]string{ var CriuCgMode_name = map[int32]string{
0: "IGNORE", 0: "IGNORE",
1: "NONE", 1: "CG_NONE",
2: "PROPS", 2: "PROPS",
3: "SOFT", 3: "SOFT",
4: "FULL", 4: "FULL",
@ -55,7 +65,7 @@ var CriuCgMode_name = map[int32]string{
} }
var CriuCgMode_value = map[string]int32{ var CriuCgMode_value = map[string]int32{
"IGNORE": 0, "IGNORE": 0,
"NONE": 1, "CG_NONE": 1,
"PROPS": 2, "PROPS": 2,
"SOFT": 3, "SOFT": 3,
"FULL": 4, "FULL": 4,
@ -79,6 +89,7 @@ func (x *CriuCgMode) UnmarshalJSON(data []byte) error {
*x = CriuCgMode(value) *x = CriuCgMode(value)
return nil return nil
} }
func (CriuCgMode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type CriuReqType int32 type CriuReqType int32
@ -93,19 +104,21 @@ const (
CriuReqType_CPUINFO_DUMP CriuReqType = 7 CriuReqType_CPUINFO_DUMP CriuReqType = 7
CriuReqType_CPUINFO_CHECK CriuReqType = 8 CriuReqType_CPUINFO_CHECK CriuReqType = 8
CriuReqType_FEATURE_CHECK CriuReqType = 9 CriuReqType_FEATURE_CHECK CriuReqType = 9
CriuReqType_VERSION CriuReqType = 10
) )
var CriuReqType_name = map[int32]string{ var CriuReqType_name = map[int32]string{
0: "EMPTY", 0: "EMPTY",
1: "DUMP", 1: "DUMP",
2: "RESTORE", 2: "RESTORE",
3: "CHECK", 3: "CHECK",
4: "PRE_DUMP", 4: "PRE_DUMP",
5: "PAGE_SERVER", 5: "PAGE_SERVER",
6: "NOTIFY", 6: "NOTIFY",
7: "CPUINFO_DUMP", 7: "CPUINFO_DUMP",
8: "CPUINFO_CHECK", 8: "CPUINFO_CHECK",
9: "FEATURE_CHECK", 9: "FEATURE_CHECK",
10: "VERSION",
} }
var CriuReqType_value = map[string]int32{ var CriuReqType_value = map[string]int32{
"EMPTY": 0, "EMPTY": 0,
@ -118,6 +131,7 @@ var CriuReqType_value = map[string]int32{
"CPUINFO_DUMP": 7, "CPUINFO_DUMP": 7,
"CPUINFO_CHECK": 8, "CPUINFO_CHECK": 8,
"FEATURE_CHECK": 9, "FEATURE_CHECK": 9,
"VERSION": 10,
} }
func (x CriuReqType) Enum() *CriuReqType { func (x CriuReqType) Enum() *CriuReqType {
@ -136,6 +150,7 @@ func (x *CriuReqType) UnmarshalJSON(data []byte) error {
*x = CriuReqType(value) *x = CriuReqType(value)
return nil return nil
} }
func (CriuReqType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
type CriuPageServerInfo struct { type CriuPageServerInfo struct {
Address *string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` Address *string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"`
@ -145,9 +160,10 @@ type CriuPageServerInfo struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuPageServerInfo) Reset() { *m = CriuPageServerInfo{} } func (m *CriuPageServerInfo) Reset() { *m = CriuPageServerInfo{} }
func (m *CriuPageServerInfo) String() string { return proto.CompactTextString(m) } func (m *CriuPageServerInfo) String() string { return proto.CompactTextString(m) }
func (*CriuPageServerInfo) ProtoMessage() {} func (*CriuPageServerInfo) ProtoMessage() {}
func (*CriuPageServerInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *CriuPageServerInfo) GetAddress() string { func (m *CriuPageServerInfo) GetAddress() string {
if m != nil && m.Address != nil { if m != nil && m.Address != nil {
@ -178,14 +194,15 @@ func (m *CriuPageServerInfo) GetFd() int32 {
} }
type CriuVethPair struct { type CriuVethPair struct {
IfIn *string `protobuf:"bytes,1,req,name=if_in" json:"if_in,omitempty"` IfIn *string `protobuf:"bytes,1,req,name=if_in,json=ifIn" json:"if_in,omitempty"`
IfOut *string `protobuf:"bytes,2,req,name=if_out" json:"if_out,omitempty"` IfOut *string `protobuf:"bytes,2,req,name=if_out,json=ifOut" json:"if_out,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuVethPair) Reset() { *m = CriuVethPair{} } func (m *CriuVethPair) Reset() { *m = CriuVethPair{} }
func (m *CriuVethPair) String() string { return proto.CompactTextString(m) } func (m *CriuVethPair) String() string { return proto.CompactTextString(m) }
func (*CriuVethPair) ProtoMessage() {} func (*CriuVethPair) ProtoMessage() {}
func (*CriuVethPair) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *CriuVethPair) GetIfIn() string { func (m *CriuVethPair) GetIfIn() string {
if m != nil && m.IfIn != nil { if m != nil && m.IfIn != nil {
@ -207,9 +224,10 @@ type ExtMountMap struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ExtMountMap) Reset() { *m = ExtMountMap{} } func (m *ExtMountMap) Reset() { *m = ExtMountMap{} }
func (m *ExtMountMap) String() string { return proto.CompactTextString(m) } func (m *ExtMountMap) String() string { return proto.CompactTextString(m) }
func (*ExtMountMap) ProtoMessage() {} func (*ExtMountMap) ProtoMessage() {}
func (*ExtMountMap) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *ExtMountMap) GetKey() string { func (m *ExtMountMap) GetKey() string {
if m != nil && m.Key != nil { if m != nil && m.Key != nil {
@ -225,15 +243,49 @@ func (m *ExtMountMap) GetVal() string {
return "" return ""
} }
type JoinNamespace struct {
Ns *string `protobuf:"bytes,1,req,name=ns" json:"ns,omitempty"`
NsFile *string `protobuf:"bytes,2,req,name=ns_file,json=nsFile" json:"ns_file,omitempty"`
ExtraOpt *string `protobuf:"bytes,3,opt,name=extra_opt,json=extraOpt" json:"extra_opt,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *JoinNamespace) Reset() { *m = JoinNamespace{} }
func (m *JoinNamespace) String() string { return proto.CompactTextString(m) }
func (*JoinNamespace) ProtoMessage() {}
func (*JoinNamespace) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *JoinNamespace) GetNs() string {
if m != nil && m.Ns != nil {
return *m.Ns
}
return ""
}
func (m *JoinNamespace) GetNsFile() string {
if m != nil && m.NsFile != nil {
return *m.NsFile
}
return ""
}
func (m *JoinNamespace) GetExtraOpt() string {
if m != nil && m.ExtraOpt != nil {
return *m.ExtraOpt
}
return ""
}
type InheritFd struct { type InheritFd struct {
Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"`
Fd *int32 `protobuf:"varint,2,req,name=fd" json:"fd,omitempty"` Fd *int32 `protobuf:"varint,2,req,name=fd" json:"fd,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *InheritFd) Reset() { *m = InheritFd{} } func (m *InheritFd) Reset() { *m = InheritFd{} }
func (m *InheritFd) String() string { return proto.CompactTextString(m) } func (m *InheritFd) String() string { return proto.CompactTextString(m) }
func (*InheritFd) ProtoMessage() {} func (*InheritFd) ProtoMessage() {}
func (*InheritFd) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *InheritFd) GetKey() string { func (m *InheritFd) GetKey() string {
if m != nil && m.Key != nil { if m != nil && m.Key != nil {
@ -255,9 +307,10 @@ type CgroupRoot struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CgroupRoot) Reset() { *m = CgroupRoot{} } func (m *CgroupRoot) Reset() { *m = CgroupRoot{} }
func (m *CgroupRoot) String() string { return proto.CompactTextString(m) } func (m *CgroupRoot) String() string { return proto.CompactTextString(m) }
func (*CgroupRoot) ProtoMessage() {} func (*CgroupRoot) ProtoMessage() {}
func (*CgroupRoot) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *CgroupRoot) GetCtrl() string { func (m *CgroupRoot) GetCtrl() string {
if m != nil && m.Ctrl != nil { if m != nil && m.Ctrl != nil {
@ -278,9 +331,10 @@ type UnixSk struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *UnixSk) Reset() { *m = UnixSk{} } func (m *UnixSk) Reset() { *m = UnixSk{} }
func (m *UnixSk) String() string { return proto.CompactTextString(m) } func (m *UnixSk) String() string { return proto.CompactTextString(m) }
func (*UnixSk) ProtoMessage() {} func (*UnixSk) ProtoMessage() {}
func (*UnixSk) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *UnixSk) GetInode() uint32 { func (m *UnixSk) GetInode() uint32 {
if m != nil && m.Inode != nil { if m != nil && m.Inode != nil {
@ -290,51 +344,62 @@ func (m *UnixSk) GetInode() uint32 {
} }
type CriuOpts struct { type CriuOpts struct {
ImagesDirFd *int32 `protobuf:"varint,1,req,name=images_dir_fd" json:"images_dir_fd,omitempty"` ImagesDirFd *int32 `protobuf:"varint,1,req,name=images_dir_fd,json=imagesDirFd" json:"images_dir_fd,omitempty"`
Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"` Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"`
LeaveRunning *bool `protobuf:"varint,3,opt,name=leave_running" json:"leave_running,omitempty"` LeaveRunning *bool `protobuf:"varint,3,opt,name=leave_running,json=leaveRunning" json:"leave_running,omitempty"`
ExtUnixSk *bool `protobuf:"varint,4,opt,name=ext_unix_sk" json:"ext_unix_sk,omitempty"` ExtUnixSk *bool `protobuf:"varint,4,opt,name=ext_unix_sk,json=extUnixSk" json:"ext_unix_sk,omitempty"`
TcpEstablished *bool `protobuf:"varint,5,opt,name=tcp_established" json:"tcp_established,omitempty"` TcpEstablished *bool `protobuf:"varint,5,opt,name=tcp_established,json=tcpEstablished" json:"tcp_established,omitempty"`
EvasiveDevices *bool `protobuf:"varint,6,opt,name=evasive_devices" json:"evasive_devices,omitempty"` EvasiveDevices *bool `protobuf:"varint,6,opt,name=evasive_devices,json=evasiveDevices" json:"evasive_devices,omitempty"`
ShellJob *bool `protobuf:"varint,7,opt,name=shell_job" json:"shell_job,omitempty"` ShellJob *bool `protobuf:"varint,7,opt,name=shell_job,json=shellJob" json:"shell_job,omitempty"`
FileLocks *bool `protobuf:"varint,8,opt,name=file_locks" json:"file_locks,omitempty"` FileLocks *bool `protobuf:"varint,8,opt,name=file_locks,json=fileLocks" json:"file_locks,omitempty"`
LogLevel *int32 `protobuf:"varint,9,opt,name=log_level,def=2" json:"log_level,omitempty"` LogLevel *int32 `protobuf:"varint,9,opt,name=log_level,json=logLevel,def=2" json:"log_level,omitempty"`
LogFile *string `protobuf:"bytes,10,opt,name=log_file" json:"log_file,omitempty"` LogFile *string `protobuf:"bytes,10,opt,name=log_file,json=logFile" json:"log_file,omitempty"`
Ps *CriuPageServerInfo `protobuf:"bytes,11,opt,name=ps" json:"ps,omitempty"` Ps *CriuPageServerInfo `protobuf:"bytes,11,opt,name=ps" json:"ps,omitempty"`
NotifyScripts *bool `protobuf:"varint,12,opt,name=notify_scripts" json:"notify_scripts,omitempty"` NotifyScripts *bool `protobuf:"varint,12,opt,name=notify_scripts,json=notifyScripts" json:"notify_scripts,omitempty"`
Root *string `protobuf:"bytes,13,opt,name=root" json:"root,omitempty"` Root *string `protobuf:"bytes,13,opt,name=root" json:"root,omitempty"`
ParentImg *string `protobuf:"bytes,14,opt,name=parent_img" json:"parent_img,omitempty"` ParentImg *string `protobuf:"bytes,14,opt,name=parent_img,json=parentImg" json:"parent_img,omitempty"`
TrackMem *bool `protobuf:"varint,15,opt,name=track_mem" json:"track_mem,omitempty"` TrackMem *bool `protobuf:"varint,15,opt,name=track_mem,json=trackMem" json:"track_mem,omitempty"`
AutoDedup *bool `protobuf:"varint,16,opt,name=auto_dedup" json:"auto_dedup,omitempty"` AutoDedup *bool `protobuf:"varint,16,opt,name=auto_dedup,json=autoDedup" json:"auto_dedup,omitempty"`
WorkDirFd *int32 `protobuf:"varint,17,opt,name=work_dir_fd" json:"work_dir_fd,omitempty"` WorkDirFd *int32 `protobuf:"varint,17,opt,name=work_dir_fd,json=workDirFd" json:"work_dir_fd,omitempty"`
LinkRemap *bool `protobuf:"varint,18,opt,name=link_remap" json:"link_remap,omitempty"` LinkRemap *bool `protobuf:"varint,18,opt,name=link_remap,json=linkRemap" json:"link_remap,omitempty"`
Veths []*CriuVethPair `protobuf:"bytes,19,rep,name=veths" json:"veths,omitempty"` Veths []*CriuVethPair `protobuf:"bytes,19,rep,name=veths" json:"veths,omitempty"`
CpuCap *uint32 `protobuf:"varint,20,opt,name=cpu_cap,def=4294967295" json:"cpu_cap,omitempty"` CpuCap *uint32 `protobuf:"varint,20,opt,name=cpu_cap,json=cpuCap,def=4294967295" json:"cpu_cap,omitempty"`
ForceIrmap *bool `protobuf:"varint,21,opt,name=force_irmap" json:"force_irmap,omitempty"` ForceIrmap *bool `protobuf:"varint,21,opt,name=force_irmap,json=forceIrmap" json:"force_irmap,omitempty"`
ExecCmd []string `protobuf:"bytes,22,rep,name=exec_cmd" json:"exec_cmd,omitempty"` ExecCmd []string `protobuf:"bytes,22,rep,name=exec_cmd,json=execCmd" json:"exec_cmd,omitempty"`
ExtMnt []*ExtMountMap `protobuf:"bytes,23,rep,name=ext_mnt" json:"ext_mnt,omitempty"` ExtMnt []*ExtMountMap `protobuf:"bytes,23,rep,name=ext_mnt,json=extMnt" json:"ext_mnt,omitempty"`
ManageCgroups *bool `protobuf:"varint,24,opt,name=manage_cgroups" json:"manage_cgroups,omitempty"` ManageCgroups *bool `protobuf:"varint,24,opt,name=manage_cgroups,json=manageCgroups" json:"manage_cgroups,omitempty"`
CgRoot []*CgroupRoot `protobuf:"bytes,25,rep,name=cg_root" json:"cg_root,omitempty"` CgRoot []*CgroupRoot `protobuf:"bytes,25,rep,name=cg_root,json=cgRoot" json:"cg_root,omitempty"`
RstSibling *bool `protobuf:"varint,26,opt,name=rst_sibling" json:"rst_sibling,omitempty"` RstSibling *bool `protobuf:"varint,26,opt,name=rst_sibling,json=rstSibling" json:"rst_sibling,omitempty"`
InheritFd []*InheritFd `protobuf:"bytes,27,rep,name=inherit_fd" json:"inherit_fd,omitempty"` InheritFd []*InheritFd `protobuf:"bytes,27,rep,name=inherit_fd,json=inheritFd" json:"inherit_fd,omitempty"`
AutoExtMnt *bool `protobuf:"varint,28,opt,name=auto_ext_mnt" json:"auto_ext_mnt,omitempty"` AutoExtMnt *bool `protobuf:"varint,28,opt,name=auto_ext_mnt,json=autoExtMnt" json:"auto_ext_mnt,omitempty"`
ExtSharing *bool `protobuf:"varint,29,opt,name=ext_sharing" json:"ext_sharing,omitempty"` ExtSharing *bool `protobuf:"varint,29,opt,name=ext_sharing,json=extSharing" json:"ext_sharing,omitempty"`
ExtMasters *bool `protobuf:"varint,30,opt,name=ext_masters" json:"ext_masters,omitempty"` ExtMasters *bool `protobuf:"varint,30,opt,name=ext_masters,json=extMasters" json:"ext_masters,omitempty"`
SkipMnt []string `protobuf:"bytes,31,rep,name=skip_mnt" json:"skip_mnt,omitempty"` SkipMnt []string `protobuf:"bytes,31,rep,name=skip_mnt,json=skipMnt" json:"skip_mnt,omitempty"`
EnableFs []string `protobuf:"bytes,32,rep,name=enable_fs" json:"enable_fs,omitempty"` EnableFs []string `protobuf:"bytes,32,rep,name=enable_fs,json=enableFs" json:"enable_fs,omitempty"`
UnixSkIno []*UnixSk `protobuf:"bytes,33,rep,name=unix_sk_ino" json:"unix_sk_ino,omitempty"` UnixSkIno []*UnixSk `protobuf:"bytes,33,rep,name=unix_sk_ino,json=unixSkIno" json:"unix_sk_ino,omitempty"`
ManageCgroupsMode *CriuCgMode `protobuf:"varint,34,opt,name=manage_cgroups_mode,enum=CriuCgMode" json:"manage_cgroups_mode,omitempty"` ManageCgroupsMode *CriuCgMode `protobuf:"varint,34,opt,name=manage_cgroups_mode,json=manageCgroupsMode,enum=CriuCgMode" json:"manage_cgroups_mode,omitempty"`
GhostLimit *uint32 `protobuf:"varint,35,opt,name=ghost_limit,def=1048576" json:"ghost_limit,omitempty"` GhostLimit *uint32 `protobuf:"varint,35,opt,name=ghost_limit,json=ghostLimit,def=1048576" json:"ghost_limit,omitempty"`
IrmapScanPaths []string `protobuf:"bytes,36,rep,name=irmap_scan_paths" json:"irmap_scan_paths,omitempty"` IrmapScanPaths []string `protobuf:"bytes,36,rep,name=irmap_scan_paths,json=irmapScanPaths" json:"irmap_scan_paths,omitempty"`
External []string `protobuf:"bytes,37,rep,name=external" json:"external,omitempty"` External []string `protobuf:"bytes,37,rep,name=external" json:"external,omitempty"`
EmptyNs *uint32 `protobuf:"varint,38,opt,name=empty_ns" json:"empty_ns,omitempty"` EmptyNs *uint32 `protobuf:"varint,38,opt,name=empty_ns,json=emptyNs" json:"empty_ns,omitempty"`
NoSeccomp *bool `protobuf:"varint,39,opt,name=no_seccomp" json:"no_seccomp,omitempty"` JoinNs []*JoinNamespace `protobuf:"bytes,39,rep,name=join_ns,json=joinNs" json:"join_ns,omitempty"`
XXX_unrecognized []byte `json:"-"` CgroupProps *string `protobuf:"bytes,41,opt,name=cgroup_props,json=cgroupProps" json:"cgroup_props,omitempty"`
CgroupPropsFile *string `protobuf:"bytes,42,opt,name=cgroup_props_file,json=cgroupPropsFile" json:"cgroup_props_file,omitempty"`
CgroupDumpController []string `protobuf:"bytes,43,rep,name=cgroup_dump_controller,json=cgroupDumpController" json:"cgroup_dump_controller,omitempty"`
FreezeCgroup *string `protobuf:"bytes,44,opt,name=freeze_cgroup,json=freezeCgroup" json:"freeze_cgroup,omitempty"`
Timeout *uint32 `protobuf:"varint,45,opt,name=timeout" json:"timeout,omitempty"`
TcpSkipInFlight *bool `protobuf:"varint,46,opt,name=tcp_skip_in_flight,json=tcpSkipInFlight" json:"tcp_skip_in_flight,omitempty"`
WeakSysctls *bool `protobuf:"varint,47,opt,name=weak_sysctls,json=weakSysctls" json:"weak_sysctls,omitempty"`
LazyPages *bool `protobuf:"varint,48,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"`
StatusFd *int32 `protobuf:"varint,49,opt,name=status_fd,json=statusFd" json:"status_fd,omitempty"`
OrphanPtsMaster *bool `protobuf:"varint,50,opt,name=orphan_pts_master,json=orphanPtsMaster" json:"orphan_pts_master,omitempty"`
XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuOpts) Reset() { *m = CriuOpts{} } func (m *CriuOpts) Reset() { *m = CriuOpts{} }
func (m *CriuOpts) String() string { return proto.CompactTextString(m) } func (m *CriuOpts) String() string { return proto.CompactTextString(m) }
func (*CriuOpts) ProtoMessage() {} func (*CriuOpts) ProtoMessage() {}
func (*CriuOpts) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
const Default_CriuOpts_LogLevel int32 = 2 const Default_CriuOpts_LogLevel int32 = 2
const Default_CriuOpts_CpuCap uint32 = 4294967295 const Default_CriuOpts_CpuCap uint32 = 4294967295
@ -606,9 +671,79 @@ func (m *CriuOpts) GetEmptyNs() uint32 {
return 0 return 0
} }
func (m *CriuOpts) GetNoSeccomp() bool { func (m *CriuOpts) GetJoinNs() []*JoinNamespace {
if m != nil && m.NoSeccomp != nil { if m != nil {
return *m.NoSeccomp return m.JoinNs
}
return nil
}
func (m *CriuOpts) GetCgroupProps() string {
if m != nil && m.CgroupProps != nil {
return *m.CgroupProps
}
return ""
}
func (m *CriuOpts) GetCgroupPropsFile() string {
if m != nil && m.CgroupPropsFile != nil {
return *m.CgroupPropsFile
}
return ""
}
func (m *CriuOpts) GetCgroupDumpController() []string {
if m != nil {
return m.CgroupDumpController
}
return nil
}
func (m *CriuOpts) GetFreezeCgroup() string {
if m != nil && m.FreezeCgroup != nil {
return *m.FreezeCgroup
}
return ""
}
func (m *CriuOpts) GetTimeout() uint32 {
if m != nil && m.Timeout != nil {
return *m.Timeout
}
return 0
}
func (m *CriuOpts) GetTcpSkipInFlight() bool {
if m != nil && m.TcpSkipInFlight != nil {
return *m.TcpSkipInFlight
}
return false
}
func (m *CriuOpts) GetWeakSysctls() bool {
if m != nil && m.WeakSysctls != nil {
return *m.WeakSysctls
}
return false
}
func (m *CriuOpts) GetLazyPages() bool {
if m != nil && m.LazyPages != nil {
return *m.LazyPages
}
return false
}
func (m *CriuOpts) GetStatusFd() int32 {
if m != nil && m.StatusFd != nil {
return *m.StatusFd
}
return 0
}
func (m *CriuOpts) GetOrphanPtsMaster() bool {
if m != nil && m.OrphanPtsMaster != nil {
return *m.OrphanPtsMaster
} }
return false return false
} }
@ -618,9 +753,10 @@ type CriuDumpResp struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuDumpResp) Reset() { *m = CriuDumpResp{} } func (m *CriuDumpResp) Reset() { *m = CriuDumpResp{} }
func (m *CriuDumpResp) String() string { return proto.CompactTextString(m) } func (m *CriuDumpResp) String() string { return proto.CompactTextString(m) }
func (*CriuDumpResp) ProtoMessage() {} func (*CriuDumpResp) ProtoMessage() {}
func (*CriuDumpResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *CriuDumpResp) GetRestored() bool { func (m *CriuDumpResp) GetRestored() bool {
if m != nil && m.Restored != nil { if m != nil && m.Restored != nil {
@ -634,9 +770,10 @@ type CriuRestoreResp struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuRestoreResp) Reset() { *m = CriuRestoreResp{} } func (m *CriuRestoreResp) Reset() { *m = CriuRestoreResp{} }
func (m *CriuRestoreResp) String() string { return proto.CompactTextString(m) } func (m *CriuRestoreResp) String() string { return proto.CompactTextString(m) }
func (*CriuRestoreResp) ProtoMessage() {} func (*CriuRestoreResp) ProtoMessage() {}
func (*CriuRestoreResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
func (m *CriuRestoreResp) GetPid() int32 { func (m *CriuRestoreResp) GetPid() int32 {
if m != nil && m.Pid != nil { if m != nil && m.Pid != nil {
@ -651,9 +788,10 @@ type CriuNotify struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuNotify) Reset() { *m = CriuNotify{} } func (m *CriuNotify) Reset() { *m = CriuNotify{} }
func (m *CriuNotify) String() string { return proto.CompactTextString(m) } func (m *CriuNotify) String() string { return proto.CompactTextString(m) }
func (*CriuNotify) ProtoMessage() {} func (*CriuNotify) ProtoMessage() {}
func (*CriuNotify) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
func (m *CriuNotify) GetScript() string { func (m *CriuNotify) GetScript() string {
if m != nil && m.Script != nil { if m != nil && m.Script != nil {
@ -673,13 +811,15 @@ func (m *CriuNotify) GetPid() int32 {
// List of features which can queried via // List of features which can queried via
// CRIU_REQ_TYPE__FEATURE_CHECK // CRIU_REQ_TYPE__FEATURE_CHECK
type CriuFeatures struct { type CriuFeatures struct {
MemTrack *bool `protobuf:"varint,1,opt,name=mem_track" json:"mem_track,omitempty"` MemTrack *bool `protobuf:"varint,1,opt,name=mem_track,json=memTrack" json:"mem_track,omitempty"`
LazyPages *bool `protobuf:"varint,2,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuFeatures) Reset() { *m = CriuFeatures{} } func (m *CriuFeatures) Reset() { *m = CriuFeatures{} }
func (m *CriuFeatures) String() string { return proto.CompactTextString(m) } func (m *CriuFeatures) String() string { return proto.CompactTextString(m) }
func (*CriuFeatures) ProtoMessage() {} func (*CriuFeatures) ProtoMessage() {}
func (*CriuFeatures) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
func (m *CriuFeatures) GetMemTrack() bool { func (m *CriuFeatures) GetMemTrack() bool {
if m != nil && m.MemTrack != nil { if m != nil && m.MemTrack != nil {
@ -688,15 +828,22 @@ func (m *CriuFeatures) GetMemTrack() bool {
return false return false
} }
func (m *CriuFeatures) GetLazyPages() bool {
if m != nil && m.LazyPages != nil {
return *m.LazyPages
}
return false
}
type CriuReq struct { type CriuReq struct {
Type *CriuReqType `protobuf:"varint,1,req,name=type,enum=CriuReqType" json:"type,omitempty"` Type *CriuReqType `protobuf:"varint,1,req,name=type,enum=CriuReqType" json:"type,omitempty"`
Opts *CriuOpts `protobuf:"bytes,2,opt,name=opts" json:"opts,omitempty"` Opts *CriuOpts `protobuf:"bytes,2,opt,name=opts" json:"opts,omitempty"`
NotifySuccess *bool `protobuf:"varint,3,opt,name=notify_success" json:"notify_success,omitempty"` NotifySuccess *bool `protobuf:"varint,3,opt,name=notify_success,json=notifySuccess" json:"notify_success,omitempty"`
// //
// When set service won't close the connection but // When set service won't close the connection but
// will wait for more req-s to appear. Works not // will wait for more req-s to appear. Works not
// for all request types. // for all request types.
KeepOpen *bool `protobuf:"varint,4,opt,name=keep_open" json:"keep_open,omitempty"` KeepOpen *bool `protobuf:"varint,4,opt,name=keep_open,json=keepOpen" json:"keep_open,omitempty"`
// //
// 'features' can be used to query which features // 'features' can be used to query which features
// are supported by the installed criu/kernel // are supported by the installed criu/kernel
@ -705,9 +852,10 @@ type CriuReq struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuReq) Reset() { *m = CriuReq{} } func (m *CriuReq) Reset() { *m = CriuReq{} }
func (m *CriuReq) String() string { return proto.CompactTextString(m) } func (m *CriuReq) String() string { return proto.CompactTextString(m) }
func (*CriuReq) ProtoMessage() {} func (*CriuReq) ProtoMessage() {}
func (*CriuReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (m *CriuReq) GetType() CriuReqType { func (m *CriuReq) GetType() CriuReqType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
@ -751,14 +899,17 @@ type CriuResp struct {
Restore *CriuRestoreResp `protobuf:"bytes,4,opt,name=restore" json:"restore,omitempty"` Restore *CriuRestoreResp `protobuf:"bytes,4,opt,name=restore" json:"restore,omitempty"`
Notify *CriuNotify `protobuf:"bytes,5,opt,name=notify" json:"notify,omitempty"` Notify *CriuNotify `protobuf:"bytes,5,opt,name=notify" json:"notify,omitempty"`
Ps *CriuPageServerInfo `protobuf:"bytes,6,opt,name=ps" json:"ps,omitempty"` Ps *CriuPageServerInfo `protobuf:"bytes,6,opt,name=ps" json:"ps,omitempty"`
CrErrno *int32 `protobuf:"varint,7,opt,name=cr_errno" json:"cr_errno,omitempty"` CrErrno *int32 `protobuf:"varint,7,opt,name=cr_errno,json=crErrno" json:"cr_errno,omitempty"`
Features *CriuFeatures `protobuf:"bytes,8,opt,name=features" json:"features,omitempty"` Features *CriuFeatures `protobuf:"bytes,8,opt,name=features" json:"features,omitempty"`
CrErrmsg *string `protobuf:"bytes,9,opt,name=cr_errmsg,json=crErrmsg" json:"cr_errmsg,omitempty"`
Version *CriuVersion `protobuf:"bytes,10,opt,name=version" json:"version,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CriuResp) Reset() { *m = CriuResp{} } func (m *CriuResp) Reset() { *m = CriuResp{} }
func (m *CriuResp) String() string { return proto.CompactTextString(m) } func (m *CriuResp) String() string { return proto.CompactTextString(m) }
func (*CriuResp) ProtoMessage() {} func (*CriuResp) ProtoMessage() {}
func (*CriuResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
func (m *CriuResp) GetType() CriuReqType { func (m *CriuResp) GetType() CriuReqType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
@ -816,7 +967,212 @@ func (m *CriuResp) GetFeatures() *CriuFeatures {
return nil return nil
} }
func (m *CriuResp) GetCrErrmsg() string {
if m != nil && m.CrErrmsg != nil {
return *m.CrErrmsg
}
return ""
}
func (m *CriuResp) GetVersion() *CriuVersion {
if m != nil {
return m.Version
}
return nil
}
// Answer for criu_req_type.VERSION requests
type CriuVersion struct {
Major *int32 `protobuf:"varint,1,req,name=major" json:"major,omitempty"`
Minor *int32 `protobuf:"varint,2,req,name=minor" json:"minor,omitempty"`
Gitid *string `protobuf:"bytes,3,opt,name=gitid" json:"gitid,omitempty"`
Sublevel *int32 `protobuf:"varint,4,opt,name=sublevel" json:"sublevel,omitempty"`
Extra *int32 `protobuf:"varint,5,opt,name=extra" json:"extra,omitempty"`
Name *string `protobuf:"bytes,6,opt,name=name" json:"name,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CriuVersion) Reset() { *m = CriuVersion{} }
func (m *CriuVersion) String() string { return proto.CompactTextString(m) }
func (*CriuVersion) ProtoMessage() {}
func (*CriuVersion) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
func (m *CriuVersion) GetMajor() int32 {
if m != nil && m.Major != nil {
return *m.Major
}
return 0
}
func (m *CriuVersion) GetMinor() int32 {
if m != nil && m.Minor != nil {
return *m.Minor
}
return 0
}
func (m *CriuVersion) GetGitid() string {
if m != nil && m.Gitid != nil {
return *m.Gitid
}
return ""
}
func (m *CriuVersion) GetSublevel() int32 {
if m != nil && m.Sublevel != nil {
return *m.Sublevel
}
return 0
}
func (m *CriuVersion) GetExtra() int32 {
if m != nil && m.Extra != nil {
return *m.Extra
}
return 0
}
func (m *CriuVersion) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
func init() { func init() {
proto.RegisterType((*CriuPageServerInfo)(nil), "criu_page_server_info")
proto.RegisterType((*CriuVethPair)(nil), "criu_veth_pair")
proto.RegisterType((*ExtMountMap)(nil), "ext_mount_map")
proto.RegisterType((*JoinNamespace)(nil), "join_namespace")
proto.RegisterType((*InheritFd)(nil), "inherit_fd")
proto.RegisterType((*CgroupRoot)(nil), "cgroup_root")
proto.RegisterType((*UnixSk)(nil), "unix_sk")
proto.RegisterType((*CriuOpts)(nil), "criu_opts")
proto.RegisterType((*CriuDumpResp)(nil), "criu_dump_resp")
proto.RegisterType((*CriuRestoreResp)(nil), "criu_restore_resp")
proto.RegisterType((*CriuNotify)(nil), "criu_notify")
proto.RegisterType((*CriuFeatures)(nil), "criu_features")
proto.RegisterType((*CriuReq)(nil), "criu_req")
proto.RegisterType((*CriuResp)(nil), "criu_resp")
proto.RegisterType((*CriuVersion)(nil), "criu_version")
proto.RegisterEnum("CriuCgMode", CriuCgMode_name, CriuCgMode_value) proto.RegisterEnum("CriuCgMode", CriuCgMode_name, CriuCgMode_value)
proto.RegisterEnum("CriuReqType", CriuReqType_name, CriuReqType_value) proto.RegisterEnum("CriuReqType", CriuReqType_name, CriuReqType_value)
} }
func init() { proto.RegisterFile("criurpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 1781 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0xdd, 0x72, 0x5b, 0xb7,
0x11, 0x0e, 0x29, 0xfe, 0x1c, 0x82, 0x3f, 0xa6, 0x10, 0xdb, 0x81, 0x93, 0xda, 0x62, 0xe8, 0x28,
0x51, 0x15, 0x97, 0x4d, 0x58, 0x3b, 0xae, 0x33, 0xed, 0x85, 0x47, 0x22, 0x5d, 0x36, 0x92, 0xc8,
0x01, 0x25, 0xcf, 0xe4, 0x0a, 0x73, 0x74, 0x0e, 0x48, 0xc1, 0x3c, 0x7f, 0x05, 0x40, 0x45, 0xf2,
0x83, 0xf4, 0x29, 0xfa, 0x0c, 0x7d, 0x84, 0xbe, 0x4e, 0x6f, 0x3b, 0xbb, 0x00, 0x65, 0x29, 0xc9,
0xb4, 0xbd, 0xc3, 0x7e, 0x58, 0x00, 0xbb, 0xfb, 0xed, 0x0f, 0x48, 0x3b, 0xd2, 0x6a, 0xad, 0x8b,
0x68, 0x50, 0xe8, 0xdc, 0xe6, 0xfd, 0x25, 0x79, 0x00, 0x80, 0x28, 0xc2, 0xa5, 0x14, 0x46, 0xea,
0x4b, 0xa9, 0x85, 0xca, 0x16, 0x39, 0x65, 0xa4, 0x1e, 0xc6, 0xb1, 0x96, 0xc6, 0xb0, 0x52, 0xaf,
0xb4, 0xd7, 0xe0, 0x1b, 0x91, 0x52, 0x52, 0x29, 0x72, 0x6d, 0x59, 0xb9, 0x57, 0xda, 0xab, 0x72,
0x5c, 0xd3, 0x2e, 0xd9, 0x2a, 0x54, 0xcc, 0xb6, 0x10, 0x82, 0x25, 0xed, 0x90, 0xf2, 0x22, 0x66,
0x15, 0x04, 0xca, 0x8b, 0xb8, 0xff, 0x27, 0xd2, 0xc1, 0x87, 0x2e, 0xa5, 0xbd, 0x10, 0x45, 0xa8,
0x34, 0xfd, 0x98, 0x54, 0xd5, 0x42, 0xa8, 0x8c, 0x95, 0x7a, 0xe5, 0xbd, 0x06, 0xaf, 0xa8, 0xc5,
0x24, 0xa3, 0x0f, 0x48, 0x4d, 0x2d, 0x44, 0xbe, 0x86, 0xeb, 0x01, 0xad, 0xaa, 0xc5, 0x74, 0x6d,
0xfb, 0x7f, 0x20, 0x6d, 0x79, 0x65, 0x45, 0x9a, 0xaf, 0x33, 0x2b, 0xd2, 0xb0, 0x80, 0x07, 0x57,
0xf2, 0xda, 0x1f, 0x85, 0x25, 0x20, 0x97, 0x61, 0xe2, 0x8f, 0xc1, 0xb2, 0xff, 0x96, 0x74, 0xde,
0xe5, 0x2a, 0x13, 0x59, 0x98, 0x4a, 0x53, 0x84, 0x91, 0x04, 0xa3, 0x32, 0xe3, 0x0f, 0x95, 0x33,
0x43, 0x3f, 0x21, 0xf5, 0xcc, 0x88, 0x85, 0x4a, 0xa4, 0x3f, 0x57, 0xcb, 0xcc, 0x58, 0x25, 0x92,
0x7e, 0x46, 0x1a, 0xf2, 0xca, 0xea, 0x50, 0xe4, 0x85, 0x45, 0xaf, 0x1a, 0x3c, 0x40, 0x60, 0x5a,
0xd8, 0xfe, 0x80, 0x10, 0x95, 0x5d, 0x48, 0xad, 0xac, 0x58, 0xc4, 0xbf, 0x62, 0x89, 0x73, 0x1d,
0x2e, 0x74, 0xae, 0xbf, 0x20, 0xcd, 0x68, 0xa9, 0xf3, 0x75, 0x21, 0x74, 0x9e, 0x5b, 0x88, 0x5f,
0x64, 0x75, 0xe2, 0xc3, 0x8a, 0x6b, 0x8c, 0x69, 0x68, 0x2f, 0xbc, 0x15, 0xb8, 0xee, 0xef, 0x90,
0xfa, 0x3a, 0x53, 0x57, 0xc2, 0xac, 0xe8, 0x7d, 0x52, 0x55, 0x59, 0x1e, 0x4b, 0x7c, 0xa5, 0xcd,
0x9d, 0xd0, 0xff, 0x57, 0x9b, 0x34, 0x30, 0xa6, 0x79, 0x61, 0x0d, 0xed, 0x93, 0xb6, 0x4a, 0xc3,
0xa5, 0x34, 0x22, 0x56, 0x5a, 0x2c, 0x62, 0xd4, 0xad, 0xf2, 0xa6, 0x03, 0x0f, 0x95, 0x1e, 0xc7,
0x1b, 0x9a, 0xca, 0x1f, 0x68, 0x7a, 0x4a, 0xda, 0x89, 0x0c, 0x2f, 0xa5, 0xd0, 0xeb, 0x2c, 0x53,
0xd9, 0x12, 0x9d, 0x0d, 0x78, 0x0b, 0x41, 0xee, 0x30, 0xfa, 0x84, 0x34, 0x21, 0xfa, 0xde, 0x1a,
0x24, 0x35, 0xe0, 0x10, 0xa0, 0xb3, 0x4c, 0x5d, 0xcd, 0x57, 0xf4, 0x2b, 0x72, 0xcf, 0x46, 0x85,
0x90, 0xc6, 0x86, 0xe7, 0x89, 0x32, 0x17, 0x32, 0x66, 0x55, 0xd4, 0xe9, 0xd8, 0xa8, 0x18, 0x7d,
0x40, 0x41, 0x51, 0x5e, 0x86, 0x46, 0x5d, 0x4a, 0x11, 0xcb, 0x4b, 0x15, 0x49, 0xc3, 0x6a, 0x4e,
0xd1, 0xc3, 0x87, 0x0e, 0x85, 0xf8, 0x9b, 0x0b, 0x99, 0x24, 0xe2, 0x5d, 0x7e, 0xce, 0xea, 0xa8,
0x12, 0x20, 0xf0, 0xd7, 0xfc, 0x9c, 0x3e, 0x26, 0x04, 0x28, 0x13, 0x49, 0x1e, 0xad, 0x0c, 0x0b,
0x9c, 0x35, 0x80, 0x1c, 0x01, 0x40, 0x9f, 0x90, 0x46, 0x92, 0x2f, 0x45, 0x22, 0x2f, 0x65, 0xc2,
0x1a, 0xe0, 0xea, 0xf7, 0xa5, 0x21, 0x0f, 0x92, 0x7c, 0x79, 0x04, 0x10, 0x7d, 0x44, 0x60, 0xed,
0x58, 0x27, 0x2e, 0xb5, 0x93, 0x7c, 0x89, 0xb4, 0x7f, 0x49, 0xca, 0x85, 0x61, 0xcd, 0x5e, 0x69,
0xaf, 0x39, 0x7c, 0x38, 0xf8, 0xd5, 0xc2, 0xe0, 0xe5, 0xc2, 0xd0, 0x5d, 0xd2, 0xc9, 0x72, 0xab,
0x16, 0xd7, 0xc2, 0x44, 0x5a, 0x15, 0xd6, 0xb0, 0x16, 0x5a, 0xd1, 0x76, 0xe8, 0xdc, 0x81, 0xc0,
0x2a, 0x30, 0xce, 0xda, 0x8e, 0x69, 0x64, 0xff, 0x31, 0x21, 0x45, 0xa8, 0x65, 0x66, 0x85, 0x4a,
0x97, 0xac, 0x83, 0x3b, 0x0d, 0x87, 0x4c, 0xd2, 0x25, 0x38, 0x6e, 0x75, 0x18, 0xad, 0x44, 0x2a,
0x53, 0x76, 0xcf, 0x39, 0x8e, 0xc0, 0xb1, 0x4c, 0xe1, 0x6c, 0xb8, 0xb6, 0xb9, 0x88, 0x65, 0xbc,
0x2e, 0x58, 0xd7, 0x39, 0x0e, 0xc8, 0x21, 0x00, 0x40, 0xd3, 0x4f, 0xb9, 0x5e, 0x6d, 0xf8, 0xdf,
0x46, 0x96, 0x1b, 0x00, 0x39, 0xf6, 0x1f, 0x13, 0x92, 0xa8, 0x6c, 0x25, 0xb4, 0x4c, 0xc3, 0x82,
0x51, 0x77, 0x1c, 0x10, 0x0e, 0x00, 0xdd, 0x25, 0x55, 0x28, 0x4e, 0xc3, 0x3e, 0xee, 0x6d, 0xed,
0x35, 0x87, 0xf7, 0x06, 0x77, 0xeb, 0x95, 0xbb, 0x5d, 0xfa, 0x94, 0xd4, 0xa3, 0x62, 0x2d, 0xa2,
0xb0, 0x60, 0xf7, 0x7b, 0xa5, 0xbd, 0xf6, 0xf7, 0xe4, 0xf9, 0xf0, 0xd5, 0xf3, 0x57, 0xdf, 0xbd,
0x1c, 0xbe, 0x7a, 0xc1, 0x6b, 0x51, 0xb1, 0x3e, 0x08, 0x0b, 0xba, 0x43, 0x9a, 0x8b, 0x5c, 0x47,
0x52, 0x28, 0x0d, 0x6f, 0x3d, 0xc0, 0xb7, 0x08, 0x42, 0x13, 0x40, 0x80, 0x04, 0x79, 0x25, 0x23,
0x11, 0xa5, 0x31, 0x7b, 0xd8, 0xdb, 0x02, 0x12, 0x40, 0x3e, 0x48, 0x21, 0x49, 0xea, 0x58, 0xeb,
0x99, 0x65, 0x9f, 0xa0, 0x25, 0x9d, 0xc1, 0x9d, 0xda, 0xe7, 0x35, 0x79, 0x65, 0x8f, 0x33, 0x0b,
0x2c, 0xa4, 0x61, 0x06, 0xfc, 0xb8, 0xf2, 0x32, 0x8c, 0x39, 0x16, 0x1c, 0x7a, 0xe0, 0x40, 0xba,
0x4b, 0xea, 0xd1, 0x12, 0x4b, 0x8f, 0x3d, 0xc2, 0xfb, 0x5a, 0x83, 0x5b, 0xe5, 0xc8, 0x6b, 0xd1,
0x92, 0x03, 0x31, 0x3b, 0xa4, 0xa9, 0x8d, 0x15, 0x46, 0x9d, 0x27, 0x50, 0x07, 0x9f, 0x3a, 0x93,
0xb5, 0xb1, 0x73, 0x87, 0xd0, 0xfd, 0xdb, 0x65, 0xcf, 0x3e, 0xc3, 0xab, 0x9a, 0x83, 0x0f, 0x10,
0x6f, 0xf8, 0xf5, 0x38, 0xa6, 0x3d, 0xd2, 0x42, 0xa6, 0x36, 0x8e, 0xfc, 0xc6, 0xdd, 0x06, 0xd8,
0xc8, 0x19, 0xbf, 0xe3, 0x6a, 0xca, 0x5c, 0x84, 0x1a, 0x9e, 0x7b, 0xec, 0x14, 0xe4, 0x95, 0x9d,
0x3b, 0x64, 0xa3, 0x90, 0x86, 0xc6, 0x4a, 0x6d, 0xd8, 0x93, 0x1b, 0x85, 0x63, 0x87, 0x40, 0x08,
0xcd, 0x4a, 0x15, 0x78, 0xff, 0x8e, 0x0b, 0x21, 0xc8, 0x70, 0x39, 0xb4, 0xaf, 0x2c, 0x3c, 0x4f,
0xa4, 0x58, 0x18, 0xd6, 0xc3, 0xbd, 0xc0, 0x01, 0x63, 0x43, 0xf7, 0x48, 0xd3, 0x57, 0xb2, 0x50,
0x59, 0xce, 0x3e, 0x47, 0x47, 0x82, 0x81, 0xc7, 0x78, 0x63, 0x8d, 0x45, 0x3d, 0xc9, 0x72, 0xfa,
0x67, 0xf2, 0xf1, 0xdd, 0x00, 0x8b, 0x14, 0x9a, 0x50, 0xbf, 0x57, 0xda, 0xeb, 0x0c, 0xdb, 0x2e,
0x3f, 0xa2, 0x25, 0x82, 0x7c, 0xfb, 0x4e, 0xd0, 0x8f, 0xf3, 0x58, 0xc2, 0x43, 0xcb, 0x8b, 0xdc,
0x58, 0x91, 0xa8, 0x54, 0x59, 0xf6, 0x14, 0xb3, 0xa5, 0xfe, 0xed, 0x37, 0xcf, 0xff, 0xf8, 0xe2,
0xe5, 0x77, 0x9c, 0xe0, 0xde, 0x11, 0x6c, 0xd1, 0x3d, 0xd2, 0xc5, 0x44, 0x11, 0x26, 0x0a, 0x33,
0x01, 0xdd, 0xcf, 0xb0, 0x2f, 0xd0, 0xec, 0x0e, 0xe2, 0xf3, 0x28, 0xcc, 0x66, 0x80, 0xd2, 0x4f,
0x21, 0x6f, 0xac, 0xd4, 0x59, 0x98, 0xb0, 0x5d, 0xef, 0x98, 0x97, 0x31, 0xa7, 0xd2, 0xc2, 0x5e,
0x8b, 0xcc, 0xb0, 0x2f, 0xe1, 0x31, 0x5e, 0x47, 0xf9, 0x04, 0x7c, 0xae, 0xbb, 0x51, 0x60, 0xd8,
0x57, 0x3e, 0xbb, 0xef, 0x8e, 0x06, 0x5e, 0x03, 0xf9, 0xc4, 0xd0, 0xcf, 0x49, 0xcb, 0x67, 0x47,
0xa1, 0xf3, 0xc2, 0xb0, 0xdf, 0x62, 0x85, 0xfa, 0x06, 0x3e, 0x03, 0x88, 0xee, 0x93, 0xed, 0xdb,
0x2a, 0xae, 0x93, 0xec, 0xa3, 0xde, 0xbd, 0x5b, 0x7a, 0xd8, 0x51, 0x9e, 0x93, 0x87, 0x5e, 0x37,
0x5e, 0xa7, 0x85, 0x88, 0xf2, 0xcc, 0xea, 0x3c, 0x49, 0xa4, 0x66, 0x5f, 0xa3, 0xf5, 0xf7, 0xdd,
0xee, 0xe1, 0x3a, 0x2d, 0x0e, 0x6e, 0xf6, 0xa0, 0x2b, 0x2f, 0xb4, 0x94, 0xef, 0x37, 0x81, 0x67,
0xcf, 0xf0, 0xf6, 0x96, 0x03, 0x5d, 0x8c, 0x61, 0x42, 0x5b, 0x95, 0x4a, 0x98, 0x95, 0xbf, 0x73,
0xde, 0x7a, 0x91, 0x7e, 0x4d, 0x28, 0xf4, 0x63, 0xcc, 0x0e, 0x95, 0x89, 0x45, 0xa2, 0x96, 0x17,
0x96, 0x0d, 0x30, 0x83, 0xa0, 0x53, 0xcf, 0x57, 0xaa, 0x98, 0x64, 0x63, 0x84, 0xc1, 0xe1, 0x9f,
0x64, 0xb8, 0x12, 0xe6, 0xda, 0x44, 0x36, 0x31, 0xec, 0xf7, 0xa8, 0xd6, 0x04, 0x6c, 0xee, 0x20,
0x6c, 0x1c, 0xe1, 0xfb, 0x6b, 0xec, 0x85, 0x86, 0x7d, 0xe3, 0x1b, 0x47, 0xf8, 0xfe, 0x7a, 0x06,
0x00, 0x36, 0x6b, 0x1b, 0xda, 0xb5, 0x81, 0xba, 0xf8, 0x16, 0xbb, 0x4e, 0xe0, 0x80, 0x71, 0x0c,
0xc1, 0xca, 0x75, 0x71, 0x01, 0xb4, 0x5a, 0xe3, 0xb3, 0x99, 0x0d, 0x9d, 0x29, 0x6e, 0x63, 0x66,
0x8d, 0x4b, 0xe9, 0xfe, 0x33, 0xff, 0x47, 0xc0, 0x50, 0x69, 0x69, 0x0a, 0xa0, 0x5b, 0x4b, 0x63,
0x73, 0x2d, 0x63, 0x9c, 0x97, 0x01, 0xbf, 0x91, 0xfb, 0xbb, 0x64, 0x1b, 0xb5, 0x3d, 0xe0, 0x0e,
0xf8, 0x09, 0xe7, 0x66, 0x1f, 0x2c, 0xfb, 0x2f, 0x49, 0x13, 0xd5, 0x5c, 0x6b, 0xa6, 0x0f, 0x49,
0xcd, 0xf5, 0x6c, 0x3f, 0x7f, 0xbd, 0xf4, 0xcb, 0xd1, 0xd8, 0xff, 0xc1, 0xfd, 0x95, 0xc4, 0x42,
0x86, 0x76, 0xad, 0x9d, 0x9f, 0xa9, 0x4c, 0x05, 0xb6, 0xe3, 0x8d, 0x35, 0xa9, 0x4c, 0x4f, 0x41,
0xfe, 0x59, 0x8c, 0xca, 0x3f, 0x8b, 0x51, 0xff, 0x9f, 0x25, 0x12, 0x78, 0x6b, 0xff, 0x46, 0xfb,
0xa4, 0x62, 0xaf, 0x0b, 0x37, 0xcd, 0x3b, 0xc3, 0xce, 0x60, 0xb3, 0x21, 0x00, 0xe5, 0xb8, 0x47,
0x9f, 0x90, 0x0a, 0x8c, 0x75, 0xbc, 0xa9, 0x39, 0x24, 0x83, 0x9b, 0x41, 0xcf, 0x11, 0xbf, 0x3d,
0x82, 0xd6, 0x51, 0x04, 0xdf, 0xb4, 0xad, 0x3b, 0x23, 0xc8, 0x81, 0x60, 0xf3, 0x4a, 0xca, 0x42,
0xe4, 0x85, 0xcc, 0xfc, 0xe0, 0x0e, 0x00, 0x98, 0x16, 0x32, 0xa3, 0xfb, 0x24, 0xd8, 0x38, 0x87,
0x03, 0xbb, 0xb9, 0xb1, 0x65, 0x83, 0xf2, 0x9b, 0xfd, 0xfe, 0xbf, 0xcb, 0xfe, 0xb3, 0x81, 0x61,
0xfe, 0x7f, 0x3c, 0x60, 0xa4, 0xbe, 0x31, 0x0d, 0xbe, 0x35, 0x01, 0xdf, 0x88, 0xf4, 0x29, 0xa9,
0x00, 0xc5, 0x68, 0xf1, 0xcd, 0xa0, 0xb9, 0x21, 0x9d, 0xe3, 0x26, 0x7d, 0x46, 0xea, 0x9e, 0x59,
0xb4, 0xbb, 0x39, 0xa4, 0x83, 0x5f, 0xd0, 0xcd, 0x37, 0x2a, 0xf4, 0x0b, 0x52, 0x73, 0x8e, 0x7b,
0x47, 0x5a, 0x83, 0x5b, 0xa4, 0x73, 0xbf, 0xe7, 0xe7, 0x7b, 0xed, 0x7f, 0xce, 0xf7, 0x47, 0x40,
0x96, 0x90, 0x5a, 0x67, 0x39, 0xfe, 0x3e, 0xaa, 0xbc, 0x1e, 0xe9, 0x11, 0x88, 0x77, 0x62, 0x16,
0xfc, 0xf7, 0x98, 0x41, 0xf0, 0xdd, 0x35, 0xa9, 0x59, 0xe2, 0x4f, 0xa4, 0xc1, 0x03, 0xbc, 0x27,
0x35, 0x4b, 0x18, 0x73, 0x97, 0x52, 0x1b, 0x95, 0x67, 0xf8, 0x0b, 0x69, 0x6e, 0x1a, 0xaa, 0x07,
0xf9, 0x66, 0xb7, 0xff, 0xf7, 0x12, 0x69, 0xdd, 0xde, 0x81, 0xdf, 0x60, 0x1a, 0xbe, 0xcb, 0xb5,
0xcf, 0x72, 0x27, 0x20, 0xaa, 0xb2, 0x5c, 0xfb, 0x8f, 0xa7, 0x13, 0x00, 0x5d, 0x2a, 0xeb, 0xbf,
0xe6, 0x0d, 0xee, 0x04, 0x28, 0x2b, 0xb3, 0x3e, 0x77, 0x3f, 0xa4, 0x8a, 0x2f, 0x58, 0x2f, 0xc3,
0x09, 0xfc, 0xe9, 0x62, 0x20, 0xab, 0xdc, 0x09, 0xf0, 0x95, 0x81, 0x5e, 0x89, 0xb1, 0x6b, 0x70,
0x5c, 0xef, 0x0b, 0x6f, 0x97, 0x1f, 0x01, 0x94, 0x90, 0xda, 0xe4, 0xcd, 0xc9, 0x94, 0x8f, 0xba,
0x1f, 0xd1, 0x26, 0xa9, 0x1f, 0xbc, 0x11, 0x27, 0xd3, 0x93, 0x51, 0xb7, 0x44, 0x1b, 0xa4, 0x3a,
0xe3, 0xd3, 0xd9, 0xbc, 0x5b, 0xa6, 0x01, 0xa9, 0xcc, 0xa7, 0xe3, 0xd3, 0xee, 0x16, 0xac, 0xc6,
0x67, 0x47, 0x47, 0xdd, 0x0a, 0x9c, 0x9b, 0x9f, 0xf2, 0xc9, 0xc1, 0x69, 0xb7, 0x0a, 0xe7, 0x0e,
0x47, 0xe3, 0xd7, 0x67, 0x47, 0xa7, 0xdd, 0xda, 0xfe, 0x3f, 0x4a, 0xbe, 0x04, 0x37, 0x99, 0x05,
0x37, 0x8d, 0x8e, 0x67, 0xa7, 0x3f, 0x76, 0x3f, 0x82, 0xf3, 0x87, 0x67, 0xc7, 0xb3, 0x6e, 0x09,
0xce, 0xf0, 0xd1, 0xfc, 0x14, 0x1e, 0x2e, 0x83, 0xc6, 0xc1, 0x5f, 0x46, 0x07, 0x3f, 0x74, 0xb7,
0x68, 0x8b, 0x04, 0x33, 0x3e, 0x12, 0xa8, 0x55, 0xa1, 0xf7, 0x48, 0x73, 0xf6, 0xfa, 0xcd, 0x48,
0xcc, 0x47, 0xfc, 0xed, 0x88, 0x77, 0xab, 0xf0, 0xec, 0xc9, 0xf4, 0x74, 0x32, 0xfe, 0xb1, 0x5b,
0xa3, 0x5d, 0xd2, 0x3a, 0x98, 0x9d, 0x4d, 0x4e, 0xc6, 0x53, 0xa7, 0x5e, 0xa7, 0xdb, 0xa4, 0xbd,
0x41, 0xdc, 0x7d, 0x01, 0x40, 0xe3, 0xd1, 0xeb, 0xd3, 0x33, 0x3e, 0xf2, 0x50, 0x03, 0x9e, 0x7e,
0x3b, 0xe2, 0xf3, 0xc9, 0xf4, 0xa4, 0x4b, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0x5f, 0x2a, 0xaf,
0x49, 0x5b, 0x0d, 0x00, 0x00,
}

View File

@ -1,3 +1,5 @@
syntax = "proto2";
message criu_page_server_info { message criu_page_server_info {
optional string address = 1; optional string address = 1;
optional int32 port = 2; optional int32 port = 2;
@ -15,6 +17,12 @@ message ext_mount_map {
required string val = 2; required string val = 2;
}; };
message join_namespace {
required string ns = 1;
required string ns_file = 2;
optional string extra_opt = 3;
}
message inherit_fd { message inherit_fd {
required string key = 1; required string key = 1;
required int32 fd = 2; required int32 fd = 2;
@ -31,7 +39,7 @@ message unix_sk {
enum criu_cg_mode { enum criu_cg_mode {
IGNORE = 0; IGNORE = 0;
NONE = 1; CG_NONE = 1;
PROPS = 2; PROPS = 2;
SOFT = 3; SOFT = 3;
FULL = 4; FULL = 4;
@ -63,13 +71,13 @@ message criu_opts {
optional int32 work_dir_fd = 17; optional int32 work_dir_fd = 17;
optional bool link_remap = 18; optional bool link_remap = 18;
repeated criu_veth_pair veths = 19; repeated criu_veth_pair veths = 19; /* DEPRECATED, use external instead */
optional uint32 cpu_cap = 20 [default = 0xffffffff]; optional uint32 cpu_cap = 20 [default = 0xffffffff];
optional bool force_irmap = 21; optional bool force_irmap = 21;
repeated string exec_cmd = 22; repeated string exec_cmd = 22;
repeated ext_mount_map ext_mnt = 23; repeated ext_mount_map ext_mnt = 23; /* DEPRECATED, use external instead */
optional bool manage_cgroups = 24; /* backward compatibility */ optional bool manage_cgroups = 24; /* backward compatibility */
repeated cgroup_root cg_root = 25; repeated cgroup_root cg_root = 25;
@ -83,14 +91,26 @@ message criu_opts {
repeated string skip_mnt = 31; repeated string skip_mnt = 31;
repeated string enable_fs = 32; repeated string enable_fs = 32;
repeated unix_sk unix_sk_ino = 33; repeated unix_sk unix_sk_ino = 33; /* DEPRECATED, use external instead */
optional criu_cg_mode manage_cgroups_mode = 34; optional criu_cg_mode manage_cgroups_mode = 34;
optional uint32 ghost_limit = 35 [default = 0x100000]; optional uint32 ghost_limit = 35 [default = 0x100000];
repeated string irmap_scan_paths = 36; repeated string irmap_scan_paths = 36;
repeated string external = 37; repeated string external = 37;
optional uint32 empty_ns = 38; optional uint32 empty_ns = 38;
optional bool no_seccomp = 39; repeated join_namespace join_ns = 39;
optional string cgroup_props = 41;
optional string cgroup_props_file = 42;
repeated string cgroup_dump_controller = 43;
optional string freeze_cgroup = 44;
optional uint32 timeout = 45;
optional bool tcp_skip_in_flight = 46;
optional bool weak_sysctls = 47;
optional bool lazy_pages = 48;
optional int32 status_fd = 49;
optional bool orphan_pts_master = 50;
} }
message criu_dump_resp { message criu_dump_resp {
@ -120,6 +140,8 @@ enum criu_req_type {
CPUINFO_CHECK = 8; CPUINFO_CHECK = 8;
FEATURE_CHECK = 9; FEATURE_CHECK = 9;
VERSION = 10;
} }
/* /*
@ -128,6 +150,7 @@ enum criu_req_type {
*/ */
message criu_features { message criu_features {
optional bool mem_track = 1; optional bool mem_track = 1;
optional bool lazy_pages = 2;
} }
/* /*
@ -171,4 +194,16 @@ message criu_resp {
optional int32 cr_errno = 7; optional int32 cr_errno = 7;
optional criu_features features = 8; optional criu_features features = 8;
optional string cr_errmsg = 9;
optional criu_version version = 10;
}
/* Answer for criu_req_type.VERSION requests */
message criu_version {
required int32 major = 1;
required int32 minor = 2;
optional string gitid = 3;
optional int32 sublevel = 4;
optional int32 extra = 5;
optional string name = 6;
} }

View File

@ -10,7 +10,7 @@ type Factory interface {
// between 1 and 1024 characters, inclusive. // between 1 and 1024 characters, inclusive.
// //
// The id must not already be in use by an existing container. Containers created using // The id must not already be in use by an existing container. Containers created using
// a factory with the same path (and file system) must have distinct ids. // a factory with the same path (and filesystem) must have distinct ids.
// //
// Returns the new container with a running process. // Returns the new container with a running process.
// //
@ -28,7 +28,6 @@ type Factory interface {
// //
// errors: // errors:
// Path does not exist // Path does not exist
// Container is stopped
// System error // System error
Load(id string) (Container, error) Load(id string) (Container, error)

View File

@ -10,7 +10,6 @@ import (
"regexp" "regexp"
"runtime/debug" "runtime/debug"
"strconv" "strconv"
"syscall"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
@ -20,6 +19,8 @@ import (
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/configs/validate" "github.com/opencontainers/runc/libcontainer/configs/validate"
"github.com/opencontainers/runc/libcontainer/utils" "github.com/opencontainers/runc/libcontainer/utils"
"golang.org/x/sys/unix"
) )
const ( const (
@ -27,10 +28,7 @@ const (
execFifoFilename = "exec.fifo" execFifoFilename = "exec.fifo"
) )
var ( var idRegex = regexp.MustCompile(`^[\w+-\.]+$`)
idRegex = regexp.MustCompile(`^[\w+-\.]+$`)
maxIdLen = 1024
)
// InitArgs returns an options func to configure a LinuxFactory with the // InitArgs returns an options func to configure a LinuxFactory with the
// provided init binary path and arguments. // provided init binary path and arguments.
@ -95,7 +93,7 @@ func TmpfsRoot(l *LinuxFactory) error {
return err return err
} }
if !mounted { if !mounted {
if err := syscall.Mount("tmpfs", l.Root, "tmpfs", 0, ""); err != nil { if err := unix.Mount("tmpfs", l.Root, "tmpfs", 0, ""); err != nil {
return err return err
} }
} }
@ -164,14 +162,6 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
if err := l.Validator.Validate(config); err != nil { if err := l.Validator.Validate(config); err != nil {
return nil, newGenericError(err, ConfigInvalid) return nil, newGenericError(err, ConfigInvalid)
} }
uid, err := config.HostRootUID()
if err != nil {
return nil, newGenericError(err, SystemError)
}
gid, err := config.HostRootGID()
if err != nil {
return nil, newGenericError(err, SystemError)
}
containerRoot := filepath.Join(l.Root, id) containerRoot := filepath.Join(l.Root, id)
if _, err := os.Stat(containerRoot); err == nil { if _, err := os.Stat(containerRoot); err == nil {
return nil, newGenericError(fmt.Errorf("container with id exists: %v", id), IdInUse) return nil, newGenericError(fmt.Errorf("container with id exists: %v", id), IdInUse)
@ -181,7 +171,7 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
if err := os.MkdirAll(containerRoot, 0711); err != nil { if err := os.MkdirAll(containerRoot, 0711); err != nil {
return nil, newGenericError(err, SystemError) return nil, newGenericError(err, SystemError)
} }
if err := os.Chown(containerRoot, uid, gid); err != nil { if err := os.Chown(containerRoot, unix.Geteuid(), unix.Getegid()); err != nil {
return nil, newGenericError(err, SystemError) return nil, newGenericError(err, SystemError)
} }
if config.Rootless { if config.Rootless {
@ -243,10 +233,10 @@ func (l *LinuxFactory) Type() string {
// This is a low level implementation detail of the reexec and should not be consumed externally // This is a low level implementation detail of the reexec and should not be consumed externally
func (l *LinuxFactory) StartInitialization() (err error) { func (l *LinuxFactory) StartInitialization() (err error) {
var ( var (
pipefd, rootfd int pipefd, fifofd int
consoleSocket *os.File consoleSocket *os.File
envInitPipe = os.Getenv("_LIBCONTAINER_INITPIPE") envInitPipe = os.Getenv("_LIBCONTAINER_INITPIPE")
envStateDir = os.Getenv("_LIBCONTAINER_STATEDIR") envFifoFd = os.Getenv("_LIBCONTAINER_FIFOFD")
envConsole = os.Getenv("_LIBCONTAINER_CONSOLE") envConsole = os.Getenv("_LIBCONTAINER_CONSOLE")
) )
@ -262,11 +252,11 @@ func (l *LinuxFactory) StartInitialization() (err error) {
) )
defer pipe.Close() defer pipe.Close()
// Only init processes have STATEDIR. // Only init processes have FIFOFD.
rootfd = -1 fifofd = -1
if it == initStandard { if it == initStandard {
if rootfd, err = strconv.Atoi(envStateDir); err != nil { if fifofd, err = strconv.Atoi(envFifoFd); err != nil {
return fmt.Errorf("unable to convert _LIBCONTAINER_STATEDIR=%s to int: %s", envStateDir, err) return fmt.Errorf("unable to convert _LIBCONTAINER_FIFOFD=%s to int: %s", envFifoFd, err)
} }
} }
@ -301,7 +291,7 @@ func (l *LinuxFactory) StartInitialization() (err error) {
} }
}() }()
i, err := newContainerInit(it, pipe, consoleSocket, rootfd) i, err := newContainerInit(it, pipe, consoleSocket, fifofd)
if err != nil { if err != nil {
return err return err
} }
@ -330,8 +320,6 @@ func (l *LinuxFactory) validateID(id string) error {
if !idRegex.MatchString(id) { if !idRegex.MatchString(id) {
return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat) return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
} }
if len(id) > maxIdLen {
return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
}
return nil return nil
} }

View File

@ -9,16 +9,18 @@ import (
"net" "net"
"os" "os"
"strings" "strings"
"syscall" "syscall" // only for Errno
"unsafe" "unsafe"
"github.com/Sirupsen/logrus"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/runc/libcontainer/user" "github.com/opencontainers/runc/libcontainer/user"
"github.com/opencontainers/runc/libcontainer/utils" "github.com/opencontainers/runc/libcontainer/utils"
"github.com/sirupsen/logrus"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
) )
type initType string type initType string
@ -29,7 +31,8 @@ const (
) )
type pid struct { type pid struct {
Pid int `json:"pid"` Pid int `json:"pid"`
PidFirstChild int `json:"pid_first"`
} }
// network is an internal struct used to setup container networks. // network is an internal struct used to setup container networks.
@ -65,7 +68,7 @@ type initer interface {
Init() error Init() error
} }
func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDirFD int) (initer, error) { func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd int) (initer, error) {
var config *initConfig var config *initConfig
if err := json.NewDecoder(pipe).Decode(&config); err != nil { if err := json.NewDecoder(pipe).Decode(&config); err != nil {
return nil, err return nil, err
@ -84,9 +87,9 @@ func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDi
return &linuxStandardInit{ return &linuxStandardInit{
pipe: pipe, pipe: pipe,
consoleSocket: consoleSocket, consoleSocket: consoleSocket,
parentPid: syscall.Getppid(), parentPid: unix.Getppid(),
config: config, config: config,
stateDirFD: stateDirFD, fifoFd: fifoFd,
}, nil }, nil
} }
return nil, fmt.Errorf("unknown init type %q", t) return nil, fmt.Errorf("unknown init type %q", t)
@ -146,7 +149,7 @@ func finalizeNamespace(config *initConfig) error {
return err return err
} }
if config.Cwd != "" { if config.Cwd != "" {
if err := syscall.Chdir(config.Cwd); err != nil { if err := unix.Chdir(config.Cwd); err != nil {
return fmt.Errorf("chdir to cwd (%q) set in config.json failed: %v", config.Cwd, err) return fmt.Errorf("chdir to cwd (%q) set in config.json failed: %v", config.Cwd, err)
} }
} }
@ -287,7 +290,7 @@ func setupUser(config *initConfig) error {
// set the group). // set the group).
if !config.Rootless { if !config.Rootless {
suppGroups := append(execUser.Sgids, addGroups...) suppGroups := append(execUser.Sgids, addGroups...)
if err := syscall.Setgroups(suppGroups); err != nil { if err := unix.Setgroups(suppGroups); err != nil {
return err return err
} }
} }
@ -313,8 +316,8 @@ func setupUser(config *initConfig) error {
// The ownership needs to match because it is created outside of the container and needs to be // The ownership needs to match because it is created outside of the container and needs to be
// localized. // localized.
func fixStdioPermissions(config *initConfig, u *user.ExecUser) error { func fixStdioPermissions(config *initConfig, u *user.ExecUser) error {
var null syscall.Stat_t var null unix.Stat_t
if err := syscall.Stat("/dev/null", &null); err != nil { if err := unix.Stat("/dev/null", &null); err != nil {
return err return err
} }
for _, fd := range []uintptr{ for _, fd := range []uintptr{
@ -322,8 +325,8 @@ func fixStdioPermissions(config *initConfig, u *user.ExecUser) error {
os.Stderr.Fd(), os.Stderr.Fd(),
os.Stdout.Fd(), os.Stdout.Fd(),
} { } {
var s syscall.Stat_t var s unix.Stat_t
if err := syscall.Fstat(int(fd), &s); err != nil { if err := unix.Fstat(int(fd), &s); err != nil {
return err return err
} }
@ -346,7 +349,7 @@ func fixStdioPermissions(config *initConfig, u *user.ExecUser) error {
// that users expect to be able to actually use their console. Without // that users expect to be able to actually use their console. Without
// this code, you couldn't effectively run as a non-root user inside a // this code, you couldn't effectively run as a non-root user inside a
// container and also have a console set up. // container and also have a console set up.
if err := syscall.Fchown(int(fd), u.Uid, int(s.Gid)); err != nil { if err := unix.Fchown(int(fd), u.Uid, int(s.Gid)); err != nil {
return err return err
} }
} }
@ -401,7 +404,7 @@ func setupRoute(config *configs.Config) error {
func setupRlimits(limits []configs.Rlimit, pid int) error { func setupRlimits(limits []configs.Rlimit, pid int) error {
for _, rlimit := range limits { for _, rlimit := range limits {
if err := system.Prlimit(pid, rlimit.Type, syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}); err != nil { if err := system.Prlimit(pid, rlimit.Type, unix.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}); err != nil {
return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err) return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err)
} }
} }
@ -424,7 +427,7 @@ type siginfo struct {
// Its based off blockUntilWaitable in src/os/wait_waitid.go // Its based off blockUntilWaitable in src/os/wait_waitid.go
func isWaitable(pid int) (bool, error) { func isWaitable(pid int) (bool, error) {
si := &siginfo{} si := &siginfo{}
_, _, e := syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(pid), uintptr(unsafe.Pointer(si)), syscall.WEXITED|syscall.WNOWAIT|syscall.WNOHANG, 0, 0) _, _, e := unix.Syscall6(unix.SYS_WAITID, _P_PID, uintptr(pid), uintptr(unsafe.Pointer(si)), unix.WEXITED|unix.WNOWAIT|unix.WNOHANG, 0, 0)
if e != 0 { if e != 0 {
return false, os.NewSyscallError("waitid", e) return false, os.NewSyscallError("waitid", e)
} }
@ -432,15 +435,15 @@ func isWaitable(pid int) (bool, error) {
return si.si_pid != 0, nil return si.si_pid != 0, nil
} }
// isNoChildren returns true if err represents a syscall.ECHILD false otherwise // isNoChildren returns true if err represents a unix.ECHILD (formerly syscall.ECHILD) false otherwise
func isNoChildren(err error) bool { func isNoChildren(err error) bool {
switch err := err.(type) { switch err := err.(type) {
case syscall.Errno: case syscall.Errno:
if err == syscall.ECHILD { if err == unix.ECHILD {
return true return true
} }
case *os.SyscallError: case *os.SyscallError:
if err.Err == syscall.ECHILD { if err.Err == unix.ECHILD {
return true return true
} }
} }
@ -478,7 +481,7 @@ func signalAllProcesses(m cgroups.Manager, s os.Signal) error {
} }
for _, p := range procs { for _, p := range procs {
if s != syscall.SIGKILL { if s != unix.SIGKILL {
if ok, err := isWaitable(p.Pid); err != nil { if ok, err := isWaitable(p.Pid); err != nil {
if !isNoChildren(err) { if !isNoChildren(err) {
logrus.Warn("signalAllProcesses: ", p.Pid, err) logrus.Warn("signalAllProcesses: ", p.Pid, err)

View File

@ -6,30 +6,16 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"syscall"
"unsafe"
)
const KEYCTL_JOIN_SESSION_KEYRING = 1 "golang.org/x/sys/unix"
const KEYCTL_SETPERM = 5 )
const KEYCTL_DESCRIBE = 6
type KeySerial uint32 type KeySerial uint32
func JoinSessionKeyring(name string) (KeySerial, error) { func JoinSessionKeyring(name string) (KeySerial, error) {
var _name *byte sessKeyId, err := unix.KeyctlJoinSessionKeyring(name)
var err error if err != nil {
return 0, fmt.Errorf("could not create session key: %v", err)
if len(name) > 0 {
_name, err = syscall.BytePtrFromString(name)
if err != nil {
return KeySerial(0), err
}
}
sessKeyId, _, errn := syscall.Syscall(syscall.SYS_KEYCTL, KEYCTL_JOIN_SESSION_KEYRING, uintptr(unsafe.Pointer(_name)), 0)
if errn != 0 {
return 0, fmt.Errorf("could not create session key: %v", errn)
} }
return KeySerial(sessKeyId), nil return KeySerial(sessKeyId), nil
} }
@ -38,10 +24,8 @@ func JoinSessionKeyring(name string) (KeySerial, error) {
// anding the bits with the given mask (clearing permissions) and setting // anding the bits with the given mask (clearing permissions) and setting
// additional permission bits // additional permission bits
func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error { func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error {
dest := make([]byte, 1024) dest, err := unix.KeyctlString(unix.KEYCTL_DESCRIBE, int(ringId))
destBytes := unsafe.Pointer(&dest[0]) if err != nil {
if _, _, err := syscall.Syscall6(syscall.SYS_KEYCTL, uintptr(KEYCTL_DESCRIBE), uintptr(ringId), uintptr(destBytes), uintptr(len(dest)), 0, 0); err != 0 {
return err return err
} }
@ -58,7 +42,7 @@ func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error {
perm := (uint32(perm64) & mask) | setbits perm := (uint32(perm64) & mask) | setbits
if _, _, err := syscall.Syscall(syscall.SYS_KEYCTL, uintptr(KEYCTL_SETPERM), uintptr(ringId), uintptr(perm)); err != 0 { if err := unix.KeyctlSetperm(int(ringId), perm); err != nil {
return err return err
} }

View File

@ -3,9 +3,8 @@
package libcontainer package libcontainer
import ( import (
"syscall"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
) )
// list of known message types we want to send to bootstrap program // list of known message types we want to send to bootstrap program
@ -19,9 +18,6 @@ const (
SetgroupAttr uint16 = 27285 SetgroupAttr uint16 = 27285
OomScoreAdjAttr uint16 = 27286 OomScoreAdjAttr uint16 = 27286
RootlessAttr uint16 = 27287 RootlessAttr uint16 = 27287
// When syscall.NLA_HDRLEN is in gccgo, take this out.
syscall_NLA_HDRLEN = (syscall.SizeofNlAttr + syscall.NLA_ALIGNTO - 1) & ^(syscall.NLA_ALIGNTO - 1)
) )
type Int32msg struct { type Int32msg struct {
@ -43,7 +39,7 @@ func (msg *Int32msg) Serialize() []byte {
} }
func (msg *Int32msg) Len() int { func (msg *Int32msg) Len() int {
return syscall_NLA_HDRLEN + 4 return unix.NLA_HDRLEN + 4
} }
// Bytemsg has the following representation // Bytemsg has the following representation
@ -56,7 +52,7 @@ type Bytemsg struct {
func (msg *Bytemsg) Serialize() []byte { func (msg *Bytemsg) Serialize() []byte {
l := msg.Len() l := msg.Len()
buf := make([]byte, (l+syscall.NLA_ALIGNTO-1) & ^(syscall.NLA_ALIGNTO-1)) buf := make([]byte, (l+unix.NLA_ALIGNTO-1) & ^(unix.NLA_ALIGNTO-1))
native := nl.NativeEndian() native := nl.NativeEndian()
native.PutUint16(buf[0:2], uint16(l)) native.PutUint16(buf[0:2], uint16(l))
native.PutUint16(buf[2:4], msg.Type) native.PutUint16(buf[2:4], msg.Type)
@ -65,7 +61,7 @@ func (msg *Bytemsg) Serialize() []byte {
} }
func (msg *Bytemsg) Len() int { func (msg *Bytemsg) Len() int {
return syscall_NLA_HDRLEN + len(msg.Value) + 1 // null-terminated return unix.NLA_HDRLEN + len(msg.Value) + 1 // null-terminated
} }
type Boolmsg struct { type Boolmsg struct {
@ -87,5 +83,5 @@ func (msg *Boolmsg) Serialize() []byte {
} }
func (msg *Boolmsg) Len() int { func (msg *Boolmsg) Len() int {
return syscall_NLA_HDRLEN + 1 return unix.NLA_HDRLEN + 1
} }

View File

@ -7,7 +7,8 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"syscall"
"golang.org/x/sys/unix"
) )
const oomCgroupName = "memory" const oomCgroupName = "memory"
@ -25,13 +26,13 @@ func registerMemoryEvent(cgDir string, evName string, arg string) (<-chan struct
if err != nil { if err != nil {
return nil, err return nil, err
} }
fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0) fd, err := unix.Eventfd(0, unix.EFD_CLOEXEC)
if syserr != 0 { if err != nil {
evFile.Close() evFile.Close()
return nil, syserr return nil, err
} }
eventfd := os.NewFile(fd, "eventfd") eventfd := os.NewFile(uintptr(fd), "eventfd")
eventControlPath := filepath.Join(cgDir, "cgroup.event_control") eventControlPath := filepath.Join(cgDir, "cgroup.event_control")
data := fmt.Sprintf("%d %d %s", eventfd.Fd(), evFile.Fd(), arg) data := fmt.Sprintf("%d %d %s", eventfd.Fd(), evFile.Fd(), arg)
@ -43,9 +44,9 @@ func registerMemoryEvent(cgDir string, evName string, arg string) (<-chan struct
ch := make(chan struct{}) ch := make(chan struct{})
go func() { go func() {
defer func() { defer func() {
close(ch)
eventfd.Close() eventfd.Close()
evFile.Close() evFile.Close()
close(ch)
}() }()
buf := make([]byte, 8) buf := make([]byte, 8)
for { for {

View File

@ -11,12 +11,14 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strconv" "strconv"
"syscall" "syscall" // only for Signal
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/runc/libcontainer/utils" "github.com/opencontainers/runc/libcontainer/utils"
"golang.org/x/sys/unix"
) )
type parentProcess interface { type parentProcess interface {
@ -33,7 +35,7 @@ type parentProcess interface {
wait() (*os.ProcessState, error) wait() (*os.ProcessState, error)
// startTime returns the process start time. // startTime returns the process start time.
startTime() (string, error) startTime() (uint64, error)
signal(os.Signal) error signal(os.Signal) error
@ -53,8 +55,9 @@ type setnsProcess struct {
bootstrapData io.Reader bootstrapData io.Reader
} }
func (p *setnsProcess) startTime() (string, error) { func (p *setnsProcess) startTime() (uint64, error) {
return system.GetProcessStartTime(p.pid()) stat, err := system.Stat(p.pid())
return stat.StartTime, err
} }
func (p *setnsProcess) signal(sig os.Signal) error { func (p *setnsProcess) signal(sig os.Signal) error {
@ -62,7 +65,7 @@ func (p *setnsProcess) signal(sig os.Signal) error {
if !ok { if !ok {
return errors.New("os: unsupported signal type") return errors.New("os: unsupported signal type")
} }
return syscall.Kill(p.pid(), s) return unix.Kill(p.pid(), s)
} }
func (p *setnsProcess) start() (err error) { func (p *setnsProcess) start() (err error) {
@ -108,7 +111,7 @@ func (p *setnsProcess) start() (err error) {
} }
}) })
if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { if err := unix.Shutdown(int(p.parentPipe.Fd()), unix.SHUT_WR); err != nil {
return newSystemErrorWithCause(err, "calling shutdown on init pipe") return newSystemErrorWithCause(err, "calling shutdown on init pipe")
} }
// Must be done after Shutdown so the child will exit and we can wait for it. // Must be done after Shutdown so the child will exit and we can wait for it.
@ -138,6 +141,16 @@ func (p *setnsProcess) execSetns() error {
p.cmd.Wait() p.cmd.Wait()
return newSystemErrorWithCause(err, "reading pid from init pipe") return newSystemErrorWithCause(err, "reading pid from init pipe")
} }
// Clean up the zombie parent process
firstChildProcess, err := os.FindProcess(pid.PidFirstChild)
if err != nil {
return err
}
// Ignore the error in case the child has already been reaped for any reason
_, _ = firstChildProcess.Wait()
process, err := os.FindProcess(pid.Pid) process, err := os.FindProcess(pid.Pid)
if err != nil { if err != nil {
return err return err
@ -190,7 +203,6 @@ type initProcess struct {
process *Process process *Process
bootstrapData io.Reader bootstrapData io.Reader
sharePidns bool sharePidns bool
rootDir *os.File
} }
func (p *initProcess) pid() int { func (p *initProcess) pid() int {
@ -221,6 +233,16 @@ func (p *initProcess) execSetns() error {
p.cmd.Wait() p.cmd.Wait()
return err return err
} }
// Clean up the zombie parent process
firstChildProcess, err := os.FindProcess(pid.PidFirstChild)
if err != nil {
return err
}
// Ignore the error in case the child has already been reaped for any reason
_, _ = firstChildProcess.Wait()
process, err := os.FindProcess(pid.Pid) process, err := os.FindProcess(pid.Pid)
if err != nil { if err != nil {
return err return err
@ -235,7 +257,6 @@ func (p *initProcess) start() error {
err := p.cmd.Start() err := p.cmd.Start()
p.process.ops = p p.process.ops = p
p.childPipe.Close() p.childPipe.Close()
p.rootDir.Close()
if err != nil { if err != nil {
p.process.ops = nil p.process.ops = nil
return newSystemErrorWithCause(err, "starting init process command") return newSystemErrorWithCause(err, "starting init process command")
@ -280,9 +301,6 @@ func (p *initProcess) start() error {
ierr := parseSync(p.parentPipe, func(sync *syncT) error { ierr := parseSync(p.parentPipe, func(sync *syncT) error {
switch sync.Type { switch sync.Type {
case procReady: case procReady:
if err := p.manager.Set(p.config.Config); err != nil {
return newSystemErrorWithCause(err, "setting cgroup config for ready process")
}
// set rlimits, this has to be done here because we lose permissions // set rlimits, this has to be done here because we lose permissions
// to raise the limits once we enter a user-namespace // to raise the limits once we enter a user-namespace
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
@ -290,6 +308,11 @@ func (p *initProcess) start() error {
} }
// call prestart hooks // call prestart hooks
if !p.config.Config.Namespaces.Contains(configs.NEWNS) { if !p.config.Config.Namespaces.Contains(configs.NEWNS) {
// Setup cgroup before prestart hook, so that the prestart hook could apply cgroup permissions.
if err := p.manager.Set(p.config.Config); err != nil {
return newSystemErrorWithCause(err, "setting cgroup config for ready process")
}
if p.config.Config.Hooks != nil { if p.config.Config.Hooks != nil {
s := configs.HookState{ s := configs.HookState{
Version: p.container.config.Version, Version: p.container.config.Version,
@ -310,6 +333,10 @@ func (p *initProcess) start() error {
} }
sentRun = true sentRun = true
case procHooks: case procHooks:
// Setup cgroup before prestart hook, so that the prestart hook could apply cgroup permissions.
if err := p.manager.Set(p.config.Config); err != nil {
return newSystemErrorWithCause(err, "setting cgroup config for procHooks process")
}
if p.config.Config.Hooks != nil { if p.config.Config.Hooks != nil {
s := configs.HookState{ s := configs.HookState{
Version: p.container.config.Version, Version: p.container.config.Version,
@ -341,7 +368,7 @@ func (p *initProcess) start() error {
if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume { if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume {
return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process")) return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process"))
} }
if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { if err := unix.Shutdown(int(p.parentPipe.Fd()), unix.SHUT_WR); err != nil {
return newSystemErrorWithCause(err, "shutting down init pipe") return newSystemErrorWithCause(err, "shutting down init pipe")
} }
@ -360,7 +387,7 @@ func (p *initProcess) wait() (*os.ProcessState, error) {
} }
// we should kill all processes in cgroup when init is died if we use host PID namespace // we should kill all processes in cgroup when init is died if we use host PID namespace
if p.sharePidns { if p.sharePidns {
signalAllProcesses(p.manager, syscall.SIGKILL) signalAllProcesses(p.manager, unix.SIGKILL)
} }
return p.cmd.ProcessState, nil return p.cmd.ProcessState, nil
} }
@ -376,8 +403,9 @@ func (p *initProcess) terminate() error {
return err return err
} }
func (p *initProcess) startTime() (string, error) { func (p *initProcess) startTime() (uint64, error) {
return system.GetProcessStartTime(p.pid()) stat, err := system.Stat(p.pid())
return stat.StartTime, err
} }
func (p *initProcess) sendConfig() error { func (p *initProcess) sendConfig() error {
@ -409,7 +437,7 @@ func (p *initProcess) signal(sig os.Signal) error {
if !ok { if !ok {
return errors.New("os: unsupported signal type") return errors.New("os: unsupported signal type")
} }
return syscall.Kill(p.pid(), s) return unix.Kill(p.pid(), s)
} }
func (p *initProcess) setExternalDescriptors(newFds []string) { func (p *initProcess) setExternalDescriptors(newFds []string) {
@ -450,7 +478,7 @@ func (p *Process) InitializeIO(rootuid, rootgid int) (i *IO, err error) {
defer func() { defer func() {
if err != nil { if err != nil {
for _, fd := range fds { for _, fd := range fds {
syscall.Close(int(fd)) unix.Close(int(fd))
} }
} }
}() }()
@ -475,7 +503,7 @@ func (p *Process) InitializeIO(rootuid, rootgid int) (i *IO, err error) {
p.Stderr, i.Stderr = w, r p.Stderr, i.Stderr = w, r
// change ownership of the pipes incase we are in a user namespace // change ownership of the pipes incase we are in a user namespace
for _, fd := range fds { for _, fd := range fds {
if err := syscall.Fchown(int(fd), rootuid, rootgid); err != nil { if err := unix.Fchown(int(fd), rootuid, rootgid); err != nil {
return nil, err return nil, err
} }
} }

View File

@ -17,20 +17,20 @@ func newRestoredProcess(pid int, fds []string) (*restoredProcess, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
started, err := system.GetProcessStartTime(pid) stat, err := system.Stat(pid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &restoredProcess{ return &restoredProcess{
proc: proc, proc: proc,
processStartTime: started, processStartTime: stat.StartTime,
fds: fds, fds: fds,
}, nil }, nil
} }
type restoredProcess struct { type restoredProcess struct {
proc *os.Process proc *os.Process
processStartTime string processStartTime uint64
fds []string fds []string
} }
@ -60,7 +60,7 @@ func (p *restoredProcess) wait() (*os.ProcessState, error) {
return st, nil return st, nil
} }
func (p *restoredProcess) startTime() (string, error) { func (p *restoredProcess) startTime() (uint64, error) {
return p.processStartTime, nil return p.processStartTime, nil
} }
@ -81,7 +81,7 @@ func (p *restoredProcess) setExternalDescriptors(newFds []string) {
// a persisted state. // a persisted state.
type nonChildProcess struct { type nonChildProcess struct {
processPid int processPid int
processStartTime string processStartTime uint64
fds []string fds []string
} }
@ -101,7 +101,7 @@ func (p *nonChildProcess) wait() (*os.ProcessState, error) {
return nil, newGenericError(fmt.Errorf("restored process cannot be waited on"), SystemError) return nil, newGenericError(fmt.Errorf("restored process cannot be waited on"), SystemError)
} }
func (p *nonChildProcess) startTime() (string, error) { func (p *nonChildProcess) startTime() (uint64, error) {
return p.processStartTime, nil return p.processStartTime, nil
} }

View File

@ -11,7 +11,6 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"time" "time"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
@ -19,12 +18,14 @@ import (
"github.com/mrunalp/fileutils" "github.com/mrunalp/fileutils"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/label"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/selinux/go-selinux/label"
"golang.org/x/sys/unix"
) )
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
// needsSetupDev returns true if /dev needs to be set up. // needsSetupDev returns true if /dev needs to be set up.
func needsSetupDev(config *configs.Config) bool { func needsSetupDev(config *configs.Config) bool {
@ -37,9 +38,8 @@ func needsSetupDev(config *configs.Config) bool {
} }
// prepareRootfs sets up the devices, mount points, and filesystems for use // prepareRootfs sets up the devices, mount points, and filesystems for use
// inside a new mount namespace. It doesn't set anything as ro or pivot_root, // inside a new mount namespace. It doesn't set anything as ro. You must call
// because console setup happens inside the caller. You must call // finalizeRootfs after this function to finish setting up the rootfs.
// finalizeRootfs in order to finish the rootfs setup.
func prepareRootfs(pipe io.ReadWriter, config *configs.Config) (err error) { func prepareRootfs(pipe io.ReadWriter, config *configs.Config) (err error) {
if err := prepareRoot(config); err != nil { if err := prepareRoot(config); err != nil {
return newSystemErrorWithCause(err, "preparing rootfs") return newSystemErrorWithCause(err, "preparing rootfs")
@ -92,7 +92,7 @@ func prepareRootfs(pipe io.ReadWriter, config *configs.Config) (err error) {
// container. It's just cleaner to do this here (at the expense of the // container. It's just cleaner to do this here (at the expense of the
// operation not being perfectly split). // operation not being perfectly split).
if err := syscall.Chdir(config.Rootfs); err != nil { if err := unix.Chdir(config.Rootfs); err != nil {
return newSystemErrorWithCausef(err, "changing dir to %q", config.Rootfs) return newSystemErrorWithCausef(err, "changing dir to %q", config.Rootfs)
} }
@ -114,13 +114,13 @@ func prepareRootfs(pipe io.ReadWriter, config *configs.Config) (err error) {
return nil return nil
} }
// finalizeRootfs actually switches the root of the process and sets anything // finalizeRootfs sets anything to ro if necessary. You must call
// to ro if necessary. You must call prepareRootfs first. // prepareRootfs first.
func finalizeRootfs(config *configs.Config) (err error) { func finalizeRootfs(config *configs.Config) (err error) {
// remount dev as ro if specified // remount dev as ro if specified
for _, m := range config.Mounts { for _, m := range config.Mounts {
if libcontainerUtils.CleanPath(m.Destination) == "/dev" { if libcontainerUtils.CleanPath(m.Destination) == "/dev" {
if m.Flags&syscall.MS_RDONLY == syscall.MS_RDONLY { if m.Flags&unix.MS_RDONLY == unix.MS_RDONLY {
if err := remountReadonly(m); err != nil { if err := remountReadonly(m); err != nil {
return newSystemErrorWithCausef(err, "remounting %q as readonly", m.Destination) return newSystemErrorWithCausef(err, "remounting %q as readonly", m.Destination)
} }
@ -136,7 +136,7 @@ func finalizeRootfs(config *configs.Config) (err error) {
} }
} }
syscall.Umask(0022) unix.Umask(0022)
return nil return nil
} }
@ -200,14 +200,14 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
if copyUp { if copyUp {
if err := fileutils.CopyDirectory(dest, tmpDir); err != nil { if err := fileutils.CopyDirectory(dest, tmpDir); err != nil {
errMsg := fmt.Errorf("tmpcopyup: failed to copy %s to %s: %v", dest, tmpDir, err) errMsg := fmt.Errorf("tmpcopyup: failed to copy %s to %s: %v", dest, tmpDir, err)
if err1 := syscall.Unmount(tmpDir, syscall.MNT_DETACH); err1 != nil { if err1 := unix.Unmount(tmpDir, unix.MNT_DETACH); err1 != nil {
return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg)
} }
return errMsg return errMsg
} }
if err := syscall.Mount(tmpDir, dest, "", syscall.MS_MOVE, ""); err != nil { if err := unix.Mount(tmpDir, dest, "", unix.MS_MOVE, ""); err != nil {
errMsg := fmt.Errorf("tmpcopyup: failed to move mount %s to %s: %v", tmpDir, dest, err) errMsg := fmt.Errorf("tmpcopyup: failed to move mount %s to %s: %v", tmpDir, dest, err)
if err1 := syscall.Unmount(tmpDir, syscall.MNT_DETACH); err1 != nil { if err1 := unix.Unmount(tmpDir, unix.MNT_DETACH); err1 != nil {
return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg)
} }
return errMsg return errMsg
@ -246,7 +246,7 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
} }
// bind mount won't change mount options, we need remount to make mount options effective. // bind mount won't change mount options, we need remount to make mount options effective.
// first check that we have non-default options required before attempting a remount // first check that we have non-default options required before attempting a remount
if m.Flags&^(syscall.MS_REC|syscall.MS_REMOUNT|syscall.MS_BIND) != 0 { if m.Flags&^(unix.MS_REC|unix.MS_REMOUNT|unix.MS_BIND) != 0 {
// only remount if unique mount options are set // only remount if unique mount options are set
if err := remount(m, rootfs); err != nil { if err := remount(m, rootfs); err != nil {
return err return err
@ -300,13 +300,13 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
} }
} }
} }
if m.Flags&syscall.MS_RDONLY != 0 { if m.Flags&unix.MS_RDONLY != 0 {
// remount cgroup root as readonly // remount cgroup root as readonly
mcgrouproot := &configs.Mount{ mcgrouproot := &configs.Mount{
Source: m.Destination, Source: m.Destination,
Device: "bind", Device: "bind",
Destination: m.Destination, Destination: m.Destination,
Flags: defaultMountFlags | syscall.MS_RDONLY | syscall.MS_BIND, Flags: defaultMountFlags | unix.MS_RDONLY | unix.MS_BIND,
} }
if err := remount(mcgrouproot, rootfs); err != nil { if err := remount(mcgrouproot, rootfs); err != nil {
return err return err
@ -360,7 +360,7 @@ func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) {
Device: "bind", Device: "bind",
Source: filepath.Join(mm.Mountpoint, relDir), Source: filepath.Join(mm.Mountpoint, relDir),
Destination: filepath.Join(m.Destination, filepath.Base(mm.Mountpoint)), Destination: filepath.Join(m.Destination, filepath.Base(mm.Mountpoint)),
Flags: syscall.MS_BIND | syscall.MS_REC | m.Flags, Flags: unix.MS_BIND | unix.MS_REC | m.Flags,
PropagationFlags: m.PropagationFlags, PropagationFlags: m.PropagationFlags,
}) })
} }
@ -436,22 +436,22 @@ func setupDevSymlinks(rootfs string) error {
// needs to be called after we chroot/pivot into the container's rootfs so that any // needs to be called after we chroot/pivot into the container's rootfs so that any
// symlinks are resolved locally. // symlinks are resolved locally.
func reOpenDevNull() error { func reOpenDevNull() error {
var stat, devNullStat syscall.Stat_t var stat, devNullStat unix.Stat_t
file, err := os.OpenFile("/dev/null", os.O_RDWR, 0) file, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil { if err != nil {
return fmt.Errorf("Failed to open /dev/null - %s", err) return fmt.Errorf("Failed to open /dev/null - %s", err)
} }
defer file.Close() defer file.Close()
if err := syscall.Fstat(int(file.Fd()), &devNullStat); err != nil { if err := unix.Fstat(int(file.Fd()), &devNullStat); err != nil {
return err return err
} }
for fd := 0; fd < 3; fd++ { for fd := 0; fd < 3; fd++ {
if err := syscall.Fstat(fd, &stat); err != nil { if err := unix.Fstat(fd, &stat); err != nil {
return err return err
} }
if stat.Rdev == devNullStat.Rdev { if stat.Rdev == devNullStat.Rdev {
// Close and re-open the fd. // Close and re-open the fd.
if err := syscall.Dup3(int(file.Fd()), fd, 0); err != nil { if err := unix.Dup3(int(file.Fd()), fd, 0); err != nil {
return err return err
} }
} }
@ -462,16 +462,16 @@ func reOpenDevNull() error {
// Create the device nodes in the container. // Create the device nodes in the container.
func createDevices(config *configs.Config) error { func createDevices(config *configs.Config) error {
useBindMount := system.RunningInUserNS() || config.Namespaces.Contains(configs.NEWUSER) useBindMount := system.RunningInUserNS() || config.Namespaces.Contains(configs.NEWUSER)
oldMask := syscall.Umask(0000) oldMask := unix.Umask(0000)
for _, node := range config.Devices { for _, node := range config.Devices {
// containers running in a user namespace are not allowed to mknod // containers running in a user namespace are not allowed to mknod
// devices so we can just bind mount it from the host. // devices so we can just bind mount it from the host.
if err := createDeviceNode(config.Rootfs, node, useBindMount); err != nil { if err := createDeviceNode(config.Rootfs, node, useBindMount); err != nil {
syscall.Umask(oldMask) unix.Umask(oldMask)
return err return err
} }
} }
syscall.Umask(oldMask) unix.Umask(oldMask)
return nil return nil
} }
@ -483,7 +483,7 @@ func bindMountDeviceNode(dest string, node *configs.Device) error {
if f != nil { if f != nil {
f.Close() f.Close()
} }
return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "") return unix.Mount(node.Path, dest, "bind", unix.MS_BIND, "")
} }
// Creates the device node in the rootfs of the container. // Creates the device node in the rootfs of the container.
@ -511,18 +511,18 @@ func mknodDevice(dest string, node *configs.Device) error {
fileMode := node.FileMode fileMode := node.FileMode
switch node.Type { switch node.Type {
case 'c', 'u': case 'c', 'u':
fileMode |= syscall.S_IFCHR fileMode |= unix.S_IFCHR
case 'b': case 'b':
fileMode |= syscall.S_IFBLK fileMode |= unix.S_IFBLK
case 'p': case 'p':
fileMode |= syscall.S_IFIFO fileMode |= unix.S_IFIFO
default: default:
return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path) return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
} }
if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil { if err := unix.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil {
return err return err
} }
return syscall.Chown(dest, int(node.Uid), int(node.Gid)) return unix.Chown(dest, int(node.Uid), int(node.Gid))
} }
func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info { func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info {
@ -589,18 +589,18 @@ func rootfsParentMountPrivate(rootfs string) error {
// shared. Secondly when we bind mount rootfs it will propagate to // shared. Secondly when we bind mount rootfs it will propagate to
// parent namespace and we don't want that to happen. // parent namespace and we don't want that to happen.
if sharedMount { if sharedMount {
return syscall.Mount("", parentMount, "", syscall.MS_PRIVATE, "") return unix.Mount("", parentMount, "", unix.MS_PRIVATE, "")
} }
return nil return nil
} }
func prepareRoot(config *configs.Config) error { func prepareRoot(config *configs.Config) error {
flag := syscall.MS_SLAVE | syscall.MS_REC flag := unix.MS_SLAVE | unix.MS_REC
if config.RootPropagation != 0 { if config.RootPropagation != 0 {
flag = config.RootPropagation flag = config.RootPropagation
} }
if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { if err := unix.Mount("", "/", "", uintptr(flag), ""); err != nil {
return err return err
} }
@ -611,11 +611,11 @@ func prepareRoot(config *configs.Config) error {
return err return err
} }
return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "") return unix.Mount(config.Rootfs, config.Rootfs, "bind", unix.MS_BIND|unix.MS_REC, "")
} }
func setReadonly() error { func setReadonly() error {
return syscall.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "") return unix.Mount("/", "/", "bind", unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY|unix.MS_REC, "")
} }
func setupPtmx(config *configs.Config) error { func setupPtmx(config *configs.Config) error {
@ -638,24 +638,24 @@ func pivotRoot(rootfs string) error {
// with pivot_root this allows us to pivot without creating directories in // with pivot_root this allows us to pivot without creating directories in
// the rootfs. Shout-outs to the LXC developers for giving us this idea. // the rootfs. Shout-outs to the LXC developers for giving us this idea.
oldroot, err := syscall.Open("/", syscall.O_DIRECTORY|syscall.O_RDONLY, 0) oldroot, err := unix.Open("/", unix.O_DIRECTORY|unix.O_RDONLY, 0)
if err != nil { if err != nil {
return err return err
} }
defer syscall.Close(oldroot) defer unix.Close(oldroot)
newroot, err := syscall.Open(rootfs, syscall.O_DIRECTORY|syscall.O_RDONLY, 0) newroot, err := unix.Open(rootfs, unix.O_DIRECTORY|unix.O_RDONLY, 0)
if err != nil { if err != nil {
return err return err
} }
defer syscall.Close(newroot) defer unix.Close(newroot)
// Change to the new root so that the pivot_root actually acts on it. // Change to the new root so that the pivot_root actually acts on it.
if err := syscall.Fchdir(newroot); err != nil { if err := unix.Fchdir(newroot); err != nil {
return err return err
} }
if err := syscall.PivotRoot(".", "."); err != nil { if err := unix.PivotRoot(".", "."); err != nil {
return fmt.Errorf("pivot_root %s", err) return fmt.Errorf("pivot_root %s", err)
} }
@ -664,35 +664,35 @@ func pivotRoot(rootfs string) error {
// really any guarantee from the kernel what /proc/self/cwd will be after a // really any guarantee from the kernel what /proc/self/cwd will be after a
// pivot_root(2). // pivot_root(2).
if err := syscall.Fchdir(oldroot); err != nil { if err := unix.Fchdir(oldroot); err != nil {
return err return err
} }
// Make oldroot rprivate to make sure our unmounts don't propagate to the // Make oldroot rprivate to make sure our unmounts don't propagate to the
// host (and thus bork the machine). // host (and thus bork the machine).
if err := syscall.Mount("", ".", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil { if err := unix.Mount("", ".", "", unix.MS_PRIVATE|unix.MS_REC, ""); err != nil {
return err return err
} }
// Preform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd. // Preform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd.
if err := syscall.Unmount(".", syscall.MNT_DETACH); err != nil { if err := unix.Unmount(".", unix.MNT_DETACH); err != nil {
return err return err
} }
// Switch back to our shiny new root. // Switch back to our shiny new root.
if err := syscall.Chdir("/"); err != nil { if err := unix.Chdir("/"); err != nil {
return fmt.Errorf("chdir / %s", err) return fmt.Errorf("chdir / %s", err)
} }
return nil return nil
} }
func msMoveRoot(rootfs string) error { func msMoveRoot(rootfs string) error {
if err := syscall.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil { if err := unix.Mount(rootfs, "/", "", unix.MS_MOVE, ""); err != nil {
return err return err
} }
if err := syscall.Chroot("."); err != nil { if err := unix.Chroot("."); err != nil {
return err return err
} }
return syscall.Chdir("/") return unix.Chdir("/")
} }
// createIfNotExists creates a file or a directory only if it does not already exist. // createIfNotExists creates a file or a directory only if it does not already exist.
@ -717,13 +717,13 @@ func createIfNotExists(path string, isDir bool) error {
// readonlyPath will make a path read only. // readonlyPath will make a path read only.
func readonlyPath(path string) error { func readonlyPath(path string) error {
if err := syscall.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { if err := unix.Mount(path, path, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil return nil
} }
return err return err
} }
return syscall.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "") return unix.Mount(path, path, "", unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY|unix.MS_REC, "")
} }
// remountReadonly will remount an existing mount point and ensure that it is read-only. // remountReadonly will remount an existing mount point and ensure that it is read-only.
@ -733,9 +733,9 @@ func remountReadonly(m *configs.Mount) error {
flags = m.Flags flags = m.Flags
) )
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
if err := syscall.Mount("", dest, "", uintptr(flags|syscall.MS_REMOUNT|syscall.MS_RDONLY), ""); err != nil { if err := unix.Mount("", dest, "", uintptr(flags|unix.MS_REMOUNT|unix.MS_RDONLY), ""); err != nil {
switch err { switch err {
case syscall.EBUSY: case unix.EBUSY:
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
continue continue
default: default:
@ -753,9 +753,9 @@ func remountReadonly(m *configs.Mount) error {
// For files, maskPath bind mounts /dev/null over the top of the specified path. // For files, maskPath bind mounts /dev/null over the top of the specified path.
// For directories, maskPath mounts read-only tmpfs over the top of the specified path. // For directories, maskPath mounts read-only tmpfs over the top of the specified path.
func maskPath(path string) error { func maskPath(path string) error {
if err := syscall.Mount("/dev/null", path, "", syscall.MS_BIND, ""); err != nil && !os.IsNotExist(err) { if err := unix.Mount("/dev/null", path, "", unix.MS_BIND, ""); err != nil && !os.IsNotExist(err) {
if err == syscall.ENOTDIR { if err == unix.ENOTDIR {
return syscall.Mount("tmpfs", path, "tmpfs", syscall.MS_RDONLY, "") return unix.Mount("tmpfs", path, "tmpfs", unix.MS_RDONLY, "")
} }
return err return err
} }
@ -776,7 +776,7 @@ func remount(m *configs.Mount, rootfs string) error {
if !strings.HasPrefix(dest, rootfs) { if !strings.HasPrefix(dest, rootfs) {
dest = filepath.Join(rootfs, dest) dest = filepath.Join(rootfs, dest)
} }
if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { if err := unix.Mount(m.Source, dest, m.Device, uintptr(m.Flags|unix.MS_REMOUNT), ""); err != nil {
return err return err
} }
return nil return nil
@ -791,7 +791,7 @@ func mountPropagate(m *configs.Mount, rootfs string, mountLabel string) error {
flags = m.Flags flags = m.Flags
) )
if libcontainerUtils.CleanPath(dest) == "/dev" { if libcontainerUtils.CleanPath(dest) == "/dev" {
flags &= ^syscall.MS_RDONLY flags &= ^unix.MS_RDONLY
} }
copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP
@ -799,12 +799,12 @@ func mountPropagate(m *configs.Mount, rootfs string, mountLabel string) error {
dest = filepath.Join(rootfs, dest) dest = filepath.Join(rootfs, dest)
} }
if err := syscall.Mount(m.Source, dest, m.Device, uintptr(flags), data); err != nil { if err := unix.Mount(m.Source, dest, m.Device, uintptr(flags), data); err != nil {
return err return err
} }
for _, pflag := range m.PropagationFlags { for _, pflag := range m.PropagationFlags {
if err := syscall.Mount("", dest, "", uintptr(pflag), ""); err != nil { if err := unix.Mount("", dest, "", uintptr(pflag), ""); err != nil {
return err return err
} }
} }

View File

@ -7,21 +7,19 @@ import (
"fmt" "fmt"
"os" "os"
"strings" "strings"
"syscall"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
libseccomp "github.com/seccomp/libseccomp-golang" libseccomp "github.com/seccomp/libseccomp-golang"
"golang.org/x/sys/unix"
) )
var ( var (
actAllow = libseccomp.ActAllow actAllow = libseccomp.ActAllow
actTrap = libseccomp.ActTrap actTrap = libseccomp.ActTrap
actKill = libseccomp.ActKill actKill = libseccomp.ActKill
actTrace = libseccomp.ActTrace.SetReturnCode(int16(syscall.EPERM)) actTrace = libseccomp.ActTrace.SetReturnCode(int16(unix.EPERM))
actErrno = libseccomp.ActErrno.SetReturnCode(int16(syscall.EPERM)) actErrno = libseccomp.ActErrno.SetReturnCode(int16(unix.EPERM))
// SeccompModeFilter refers to the syscall argument SECCOMP_MODE_FILTER.
SeccompModeFilter = uintptr(2)
) )
// Filters given syscalls in a container, preventing them from being used // Filters given syscalls in a container, preventing them from being used
@ -84,9 +82,9 @@ func IsEnabled() bool {
s, err := parseStatusFile("/proc/self/status") s, err := parseStatusFile("/proc/self/status")
if err != nil { if err != nil {
// Check if Seccomp is supported, via CONFIG_SECCOMP. // Check if Seccomp is supported, via CONFIG_SECCOMP.
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_SECCOMP, 0, 0); err != syscall.EINVAL { if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
// Make sure the kernel has CONFIG_SECCOMP_FILTER. // Make sure the kernel has CONFIG_SECCOMP_FILTER.
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECCOMP, SeccompModeFilter, 0); err != syscall.EINVAL { if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
return true return true
} }
} }

View File

@ -8,9 +8,11 @@ import (
"github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/apparmor"
"github.com/opencontainers/runc/libcontainer/keys" "github.com/opencontainers/runc/libcontainer/keys"
"github.com/opencontainers/runc/libcontainer/label"
"github.com/opencontainers/runc/libcontainer/seccomp" "github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/selinux/go-selinux/label"
"golang.org/x/sys/unix"
) )
// linuxSetnsInit performs the container's initialization for running a new process // linuxSetnsInit performs the container's initialization for running a new process
@ -41,7 +43,7 @@ func (l *linuxSetnsInit) Init() error {
} }
} }
if l.config.NoNewPrivileges { if l.config.NoNewPrivileges {
if err := system.Prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil { if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
return err return err
} }
} }

View File

@ -6,21 +6,23 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"syscall" "syscall" //only for Exec
"github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/apparmor"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/keys" "github.com/opencontainers/runc/libcontainer/keys"
"github.com/opencontainers/runc/libcontainer/label"
"github.com/opencontainers/runc/libcontainer/seccomp" "github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/selinux/go-selinux/label"
"golang.org/x/sys/unix"
) )
type linuxStandardInit struct { type linuxStandardInit struct {
pipe *os.File pipe *os.File
consoleSocket *os.File consoleSocket *os.File
parentPid int parentPid int
stateDirFD int fifoFd int
config *initConfig config *initConfig
} }
@ -40,10 +42,6 @@ func (l *linuxStandardInit) getSessionRingParams() (string, uint32, uint32) {
return fmt.Sprintf("_ses.%s", l.config.ContainerId), 0xffffffff, newperms return fmt.Sprintf("_ses.%s", l.config.ContainerId), 0xffffffff, newperms
} }
// PR_SET_NO_NEW_PRIVS isn't exposed in Golang so we define it ourselves copying the value
// the kernel
const PR_SET_NO_NEW_PRIVS = 0x26
func (l *linuxStandardInit) Init() error { func (l *linuxStandardInit) Init() error {
if !l.config.Config.NoNewKeyring { if !l.config.Config.NoNewKeyring {
ringname, keepperms, newperms := l.getSessionRingParams() ringname, keepperms, newperms := l.getSessionRingParams()
@ -95,7 +93,7 @@ func (l *linuxStandardInit) Init() error {
} }
if hostname := l.config.Config.Hostname; hostname != "" { if hostname := l.config.Config.Hostname; hostname != "" {
if err := syscall.Sethostname([]byte(hostname)); err != nil { if err := unix.Sethostname([]byte(hostname)); err != nil {
return err return err
} }
} }
@ -126,7 +124,7 @@ func (l *linuxStandardInit) Init() error {
return err return err
} }
if l.config.NoNewPrivileges { if l.config.NoNewPrivileges {
if err := system.Prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil { if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
return err return err
} }
} }
@ -155,8 +153,8 @@ func (l *linuxStandardInit) Init() error {
// compare the parent from the initial start of the init process and make sure that it did not change. // compare the parent from the initial start of the init process and make sure that it did not change.
// if the parent changes that means it died and we were reparented to something else so we should // if the parent changes that means it died and we were reparented to something else so we should
// just kill ourself and not cause problems for someone else. // just kill ourself and not cause problems for someone else.
if syscall.Getppid() != l.parentPid { if unix.Getppid() != l.parentPid {
return syscall.Kill(syscall.Getpid(), syscall.SIGKILL) return unix.Kill(unix.Getpid(), unix.SIGKILL)
} }
// check for the arg before waiting to make sure it exists and it is returned // check for the arg before waiting to make sure it exists and it is returned
// as a create time error. // as a create time error.
@ -166,13 +164,15 @@ func (l *linuxStandardInit) Init() error {
} }
// close the pipe to signal that we have completed our init. // close the pipe to signal that we have completed our init.
l.pipe.Close() l.pipe.Close()
// wait for the fifo to be opened on the other side before // Wait for the FIFO to be opened on the other side before exec-ing the
// exec'ing the users process. // user process. We open it through /proc/self/fd/$fd, because the fd that
fd, err := syscall.Openat(l.stateDirFD, execFifoFilename, os.O_WRONLY|syscall.O_CLOEXEC, 0) // was given to us was an O_PATH fd to the fifo itself. Linux allows us to
// re-open an O_PATH fd through /proc.
fd, err := unix.Open(fmt.Sprintf("/proc/self/fd/%d", l.fifoFd), unix.O_WRONLY|unix.O_CLOEXEC, 0)
if err != nil { if err != nil {
return newSystemErrorWithCause(err, "openat exec fifo") return newSystemErrorWithCause(err, "openat exec fifo")
} }
if _, err := syscall.Write(fd, []byte("0")); err != nil { if _, err := unix.Write(fd, []byte("0")); err != nil {
return newSystemErrorWithCause(err, "write 0 exec fifo") return newSystemErrorWithCause(err, "write 0 exec fifo")
} }
if l.config.Config.Seccomp != nil && l.config.NoNewPrivileges { if l.config.Config.Seccomp != nil && l.config.NoNewPrivileges {
@ -182,7 +182,7 @@ func (l *linuxStandardInit) Init() error {
} }
// close the statedir fd before exec because the kernel resets dumpable in the wrong order // close the statedir fd before exec because the kernel resets dumpable in the wrong order
// https://github.com/torvalds/linux/blob/v4.9/fs/exec.c#L1290-L1318 // https://github.com/torvalds/linux/blob/v4.9/fs/exec.c#L1290-L1318
syscall.Close(l.stateDirFD) unix.Close(l.fifoFd)
if err := syscall.Exec(name, l.config.Args[0:], os.Environ()); err != nil { if err := syscall.Exec(name, l.config.Args[0:], os.Environ()); err != nil {
return newSystemErrorWithCause(err, "exec user process") return newSystemErrorWithCause(err, "exec user process")
} }

View File

@ -6,11 +6,12 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"syscall"
"github.com/Sirupsen/logrus"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/utils" "github.com/opencontainers/runc/libcontainer/utils"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
) )
func newStateTransitionError(from, to containerState) error { func newStateTransitionError(from, to containerState) error {
@ -39,7 +40,7 @@ type containerState interface {
func destroy(c *linuxContainer) error { func destroy(c *linuxContainer) error {
if !c.config.Namespaces.Contains(configs.NEWPID) { if !c.config.Namespaces.Contains(configs.NEWPID) {
if err := signalAllProcesses(c.cgroupManager, syscall.SIGKILL); err != nil { if err := signalAllProcesses(c.cgroupManager, unix.SIGKILL); err != nil {
logrus.Warn(err) logrus.Warn(err)
} }
} }
@ -156,7 +157,7 @@ func (i *createdState) transition(s containerState) error {
} }
func (i *createdState) destroy() error { func (i *createdState) destroy() error {
i.c.initProcess.signal(syscall.SIGKILL) i.c.initProcess.signal(unix.SIGKILL)
return destroy(i.c) return destroy(i.c)
} }

View File

@ -7,8 +7,10 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"syscall" "syscall" // only for exec
"unsafe" "unsafe"
"golang.org/x/sys/unix"
) )
// If arg2 is nonzero, set the "child subreaper" attribute of the // If arg2 is nonzero, set the "child subreaper" attribute of the
@ -53,8 +55,8 @@ func Execv(cmd string, args []string, env []string) error {
return syscall.Exec(name, args, env) return syscall.Exec(name, args, env)
} }
func Prlimit(pid, resource int, limit syscall.Rlimit) error { func Prlimit(pid, resource int, limit unix.Rlimit) error {
_, _, err := syscall.RawSyscall6(syscall.SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(&limit)), uintptr(unsafe.Pointer(&limit)), 0, 0) _, _, err := unix.RawSyscall6(unix.SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(&limit)), uintptr(unsafe.Pointer(&limit)), 0, 0)
if err != 0 { if err != 0 {
return err return err
} }
@ -62,7 +64,7 @@ func Prlimit(pid, resource int, limit syscall.Rlimit) error {
} }
func SetParentDeathSignal(sig uintptr) error { func SetParentDeathSignal(sig uintptr) error {
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, sig, 0); err != 0 { if err := unix.Prctl(unix.PR_SET_PDEATHSIG, sig, 0, 0, 0); err != nil {
return err return err
} }
return nil return nil
@ -70,15 +72,14 @@ func SetParentDeathSignal(sig uintptr) error {
func GetParentDeathSignal() (ParentDeathSignal, error) { func GetParentDeathSignal() (ParentDeathSignal, error) {
var sig int var sig int
_, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0) if err := unix.Prctl(unix.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0, 0, 0); err != nil {
if err != 0 {
return -1, err return -1, err
} }
return ParentDeathSignal(sig), nil return ParentDeathSignal(sig), nil
} }
func SetKeepCaps() error { func SetKeepCaps() error {
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_KEEPCAPS, 1, 0); err != 0 { if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 1, 0, 0, 0); err != nil {
return err return err
} }
@ -86,7 +87,7 @@ func SetKeepCaps() error {
} }
func ClearKeepCaps() error { func ClearKeepCaps() error {
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_KEEPCAPS, 0, 0); err != 0 { if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 0, 0, 0, 0); err != nil {
return err return err
} }
@ -94,7 +95,7 @@ func ClearKeepCaps() error {
} }
func Setctty() error { func Setctty() error {
if _, _, err := syscall.RawSyscall(syscall.SYS_IOCTL, 0, uintptr(syscall.TIOCSCTTY), 0); err != 0 { if err := unix.IoctlSetInt(0, unix.TIOCSCTTY, 0); err != nil {
return err return err
} }
return nil return nil
@ -131,13 +132,5 @@ func RunningInUserNS() bool {
// SetSubreaper sets the value i as the subreaper setting for the calling process // SetSubreaper sets the value i as the subreaper setting for the calling process
func SetSubreaper(i int) error { func SetSubreaper(i int) error {
return Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0) return unix.Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0)
}
func Prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
if e1 != 0 {
err = e1
}
return
} }

View File

@ -1,43 +1,113 @@
package system package system
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
) )
// look in /proc to find the process start time so that we can verify // State is the status of a process.
// that this pid has started after ourself type State rune
const ( // Only values for Linux 3.14 and later are listed here
Dead State = 'X'
DiskSleep State = 'D'
Running State = 'R'
Sleeping State = 'S'
Stopped State = 'T'
TracingStop State = 't'
Zombie State = 'Z'
)
// String forms of the state from proc(5)'s documentation for
// /proc/[pid]/status' "State" field.
func (s State) String() string {
switch s {
case Dead:
return "dead"
case DiskSleep:
return "disk sleep"
case Running:
return "running"
case Sleeping:
return "sleeping"
case Stopped:
return "stopped"
case TracingStop:
return "tracing stop"
case Zombie:
return "zombie"
default:
return fmt.Sprintf("unknown (%c)", s)
}
}
// Stat_t represents the information from /proc/[pid]/stat, as
// described in proc(5) with names based on the /proc/[pid]/status
// fields.
type Stat_t struct {
// PID is the process ID.
PID uint
// Name is the command run by the process.
Name string
// State is the state of the process.
State State
// StartTime is the number of clock ticks after system boot (since
// Linux 2.6).
StartTime uint64
}
// Stat returns a Stat_t instance for the specified process.
func Stat(pid int) (stat Stat_t, err error) {
bytes, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
if err != nil {
return stat, err
}
return parseStat(string(bytes))
}
// GetProcessStartTime is deprecated. Use Stat(pid) and
// Stat_t.StartTime instead.
func GetProcessStartTime(pid int) (string, error) { func GetProcessStartTime(pid int) (string, error) {
data, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat")) stat, err := Stat(pid)
if err != nil { if err != nil {
return "", err return "", err
} }
return parseStartTime(string(data)) return fmt.Sprintf("%d", stat.StartTime), nil
} }
func parseStartTime(stat string) (string, error) { func parseStat(data string) (stat Stat_t, err error) {
// the starttime is located at pos 22 // From proc(5), field 2 could contain space and is inside `(` and `)`.
// from the man page // The following is an example:
//
// starttime %llu (was %lu before Linux 2.6)
// (22) The time the process started after system boot. In kernels before Linux 2.6, this
// value was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks
// (divide by sysconf(_SC_CLK_TCK)).
//
// NOTE:
// pos 2 could contain space and is inside `(` and `)`:
// (2) comm %s
// The filename of the executable, in parentheses.
// This is visible whether or not the executable is
// swapped out.
//
// the following is an example:
// 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0 // 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
i := strings.LastIndex(data, ")")
if i <= 2 || i >= len(data)-1 {
return stat, fmt.Errorf("invalid stat data: %q", data)
}
// get parts after last `)`: parts := strings.SplitN(data[:i], "(", 2)
s := strings.Split(stat, ")") if len(parts) != 2 {
parts := strings.Split(strings.TrimSpace(s[len(s)-1]), " ") return stat, fmt.Errorf("invalid stat data: %q", data)
return parts[22-3], nil // starts at 3 (after the filename pos `2`) }
stat.Name = parts[1]
_, err = fmt.Sscanf(parts[0], "%d", &stat.PID)
if err != nil {
return stat, err
}
// parts indexes should be offset by 3 from the field number given
// proc(5), because parts is zero-indexed and we've removed fields
// one (PID) and two (Name) in the paren-split.
parts = strings.Split(data[i+2:], " ")
var state int
fmt.Sscanf(parts[3-3], "%c", &state)
stat.State = State(state)
fmt.Sscanf(parts[22-3], "%d", &stat.StartTime)
return stat, nil
} }

View File

@ -1,40 +0,0 @@
package system
import (
"fmt"
"runtime"
"syscall"
)
// Via http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7b21fddd087678a70ad64afc0f632e0f1071b092
//
// We need different setns values for the different platforms and arch
// We are declaring the macro here because the SETNS syscall does not exist in th stdlib
var setNsMap = map[string]uintptr{
"linux/386": 346,
"linux/arm64": 268,
"linux/amd64": 308,
"linux/arm": 375,
"linux/ppc": 350,
"linux/ppc64": 350,
"linux/ppc64le": 350,
"linux/s390x": 339,
}
var sysSetns = setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
func SysSetns() uint32 {
return uint32(sysSetns)
}
func Setns(fd uintptr, flags uintptr) error {
ns, exists := setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
if !exists {
return fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
}
_, _, err := syscall.RawSyscall(ns, fd, flags, 0)
if err != 0 {
return err
}
return nil
}

View File

@ -3,12 +3,12 @@
package system package system
import ( import (
"syscall" "golang.org/x/sys/unix"
) )
// Setuid sets the uid of the calling thread to the specified uid. // Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) { func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID32, uintptr(uid), 0, 0) _, _, e1 := unix.RawSyscall(unix.SYS_SETUID32, uintptr(uid), 0, 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }
@ -17,7 +17,7 @@ func Setuid(uid int) (err error) {
// Setgid sets the gid of the calling thread to the specified gid. // Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) { func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID32, uintptr(gid), 0, 0) _, _, e1 := unix.RawSyscall(unix.SYS_SETGID32, uintptr(gid), 0, 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }

View File

@ -3,12 +3,12 @@
package system package system
import ( import (
"syscall" "golang.org/x/sys/unix"
) )
// Setuid sets the uid of the calling thread to the specified uid. // Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) { func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0) _, _, e1 := unix.RawSyscall(unix.SYS_SETUID, uintptr(uid), 0, 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }
@ -17,7 +17,7 @@ func Setuid(uid int) (err error) {
// Setgid sets the gid of the calling thread to the specified gid. // Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) { func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID, uintptr(gid), 0, 0) _, _, e1 := unix.RawSyscall(unix.SYS_SETGID, uintptr(gid), 0, 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }

View File

@ -3,12 +3,12 @@
package system package system
import ( import (
"syscall" "golang.org/x/sys/unix"
) )
// Setuid sets the uid of the calling thread to the specified uid. // Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) { func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID32, uintptr(uid), 0, 0) _, _, e1 := unix.RawSyscall(unix.SYS_SETUID32, uintptr(uid), 0, 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }
@ -17,7 +17,7 @@ func Setuid(uid int) (err error) {
// Setgid sets the gid of the calling thread to the specified gid. // Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) { func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID32, uintptr(gid), 0, 0) _, _, e1 := unix.RawSyscall(unix.SYS_SETGID32, uintptr(gid), 0, 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }

View File

@ -1,99 +1,35 @@
package system package system
import ( import "golang.org/x/sys/unix"
"syscall"
"unsafe"
)
var _zero uintptr
// Returns the size of xattrs and nil error
// Requires path, takes allocated []byte or nil as last argument
func Llistxattr(path string, dest []byte) (size int, err error) {
pathBytes, err := syscall.BytePtrFromString(path)
if err != nil {
return -1, err
}
var newpathBytes unsafe.Pointer
if len(dest) > 0 {
newpathBytes = unsafe.Pointer(&dest[0])
} else {
newpathBytes = unsafe.Pointer(&_zero)
}
_size, _, errno := syscall.Syscall6(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(newpathBytes), uintptr(len(dest)), 0, 0, 0)
size = int(_size)
if errno != 0 {
return -1, errno
}
return size, nil
}
// Returns a []byte slice if the xattr is set and nil otherwise // Returns a []byte slice if the xattr is set and nil otherwise
// Requires path and its attribute as arguments // Requires path and its attribute as arguments
func Lgetxattr(path string, attr string) ([]byte, error) { func Lgetxattr(path string, attr string) ([]byte, error) {
var sz int var sz int
pathBytes, err := syscall.BytePtrFromString(path)
if err != nil {
return nil, err
}
attrBytes, err := syscall.BytePtrFromString(attr)
if err != nil {
return nil, err
}
// Start with a 128 length byte array // Start with a 128 length byte array
sz = 128 dest := make([]byte, 128)
dest := make([]byte, sz) sz, errno := unix.Lgetxattr(path, attr, dest)
destBytes := unsafe.Pointer(&dest[0])
_sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
switch { switch {
case errno == syscall.ENODATA: case errno == unix.ENODATA:
return nil, errno return nil, errno
case errno == syscall.ENOTSUP: case errno == unix.ENOTSUP:
return nil, errno return nil, errno
case errno == syscall.ERANGE: case errno == unix.ERANGE:
// 128 byte array might just not be good enough, // 128 byte array might just not be good enough,
// A dummy buffer is used ``uintptr(0)`` to get real size // A dummy buffer is used to get the real size
// of the xattrs on disk // of the xattrs on disk
_sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(unsafe.Pointer(nil)), uintptr(0), 0, 0) sz, errno = unix.Lgetxattr(path, attr, []byte{})
sz = int(_sz) if errno != nil {
if sz < 0 {
return nil, errno return nil, errno
} }
dest = make([]byte, sz) dest = make([]byte, sz)
destBytes := unsafe.Pointer(&dest[0]) sz, errno = unix.Lgetxattr(path, attr, dest)
_sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) if errno != nil {
if errno != 0 {
return nil, errno return nil, errno
} }
case errno != 0: case errno != nil:
return nil, errno return nil, errno
} }
sz = int(_sz)
return dest[:sz], nil return dest[:sz], nil
} }
func Lsetxattr(path string, attr string, data []byte, flags int) error {
pathBytes, err := syscall.BytePtrFromString(path)
if err != nil {
return err
}
attrBytes, err := syscall.BytePtrFromString(attr)
if err != nil {
return err
}
var dataBytes unsafe.Pointer
if len(data) > 0 {
dataBytes = unsafe.Pointer(&data[0])
} else {
dataBytes = unsafe.Pointer(&_zero)
}
_, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0)
if errno != 0 {
return errno
}
return nil
}

View File

@ -2,7 +2,6 @@ package user
import ( import (
"errors" "errors"
"syscall"
) )
var ( var (
@ -36,13 +35,6 @@ func lookupUser(filter func(u User) bool) (User, error) {
return users[0], nil return users[0], nil
} }
// CurrentUser looks up the current user by their user id in /etc/passwd. If the
// user cannot be found (or there is no /etc/passwd file on the filesystem),
// then CurrentUser returns an error.
func CurrentUser() (User, error) {
return LookupUid(syscall.Getuid())
}
// LookupUser looks up a user by their username in /etc/passwd. If the user // LookupUser looks up a user by their username in /etc/passwd. If the user
// cannot be found (or there is no /etc/passwd file on the filesystem), then // cannot be found (or there is no /etc/passwd file on the filesystem), then
// LookupUser returns an error. // LookupUser returns an error.
@ -84,13 +76,6 @@ func lookupGroup(filter func(g Group) bool) (Group, error) {
return groups[0], nil return groups[0], nil
} }
// CurrentGroup looks up the current user's group by their primary group id's
// entry in /etc/passwd. If the group cannot be found (or there is no
// /etc/group file on the filesystem), then CurrentGroup returns an error.
func CurrentGroup() (Group, error) {
return LookupGid(syscall.Getgid())
}
// LookupGroup looks up a group by its name in /etc/group. If the group cannot // LookupGroup looks up a group by its name in /etc/group. If the group cannot
// be found (or there is no /etc/group file on the filesystem), then LookupGroup // be found (or there is no /etc/group file on the filesystem), then LookupGroup
// returns an error. // returns an error.

View File

@ -5,6 +5,8 @@ package user
import ( import (
"io" "io"
"os" "os"
"golang.org/x/sys/unix"
) )
// Unix-specific path to the passwd and group formatted files. // Unix-specific path to the passwd and group formatted files.
@ -28,3 +30,17 @@ func GetGroupPath() (string, error) {
func GetGroup() (io.ReadCloser, error) { func GetGroup() (io.ReadCloser, error) {
return os.Open(unixGroupPath) return os.Open(unixGroupPath)
} }
// CurrentUser looks up the current user by their user id in /etc/passwd. If the
// user cannot be found (or there is no /etc/passwd file on the filesystem),
// then CurrentUser returns an error.
func CurrentUser() (User, error) {
return LookupUid(unix.Getuid())
}
// CurrentGroup looks up the current user's group by their primary group id's
// entry in /etc/passwd. If the group cannot be found (or there is no
// /etc/group file on the filesystem), then CurrentGroup returns an error.
func CurrentGroup() (Group, error) {
return LookupGid(unix.Getgid())
}

View File

@ -2,7 +2,10 @@
package user package user
import "io" import (
"io"
"syscall"
)
func GetPasswdPath() (string, error) { func GetPasswdPath() (string, error) {
return "", ErrUnsupported return "", ErrUnsupported
@ -19,3 +22,17 @@ func GetGroupPath() (string, error) {
func GetGroup() (io.ReadCloser, error) { func GetGroup() (io.ReadCloser, error) {
return nil, ErrUnsupported return nil, ErrUnsupported
} }
// CurrentUser looks up the current user by their user id in /etc/passwd. If the
// user cannot be found (or there is no /etc/passwd file on the filesystem),
// then CurrentUser returns an error.
func CurrentUser() (User, error) {
return LookupUid(syscall.Getuid())
}
// CurrentGroup looks up the current user's group by their primary group id's
// entry in /etc/passwd. If the group cannot be found (or there is no
// /etc/group file on the filesystem), then CurrentGroup returns an error.
func CurrentGroup() (Group, error) {
return LookupGid(syscall.Getgid())
}

View File

@ -1,148 +0,0 @@
/*
* Copyright 2016 SUSE LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "cmsg.h"
#define error(fmt, ...) \
({ \
fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__); \
errno = ECOMM; \
goto err; /* return value */ \
})
/*
* Sends a file descriptor along the sockfd provided. Returns the return
* value of sendmsg(2). Any synchronisation and preparation of state
* should be done external to this (we expect the other side to be in
* recvfd() in the code).
*/
ssize_t sendfd(int sockfd, struct file_t file)
{
struct msghdr msg = {0};
struct iovec iov[1] = {0};
struct cmsghdr *cmsg;
int *fdptr;
int ret;
union {
char buf[CMSG_SPACE(sizeof(file.fd))];
struct cmsghdr align;
} u;
/*
* We need to send some other data along with the ancillary data,
* otherwise the other side won't recieve any data. This is very
* well-hidden in the documentation (and only applies to
* SOCK_STREAM). See the bottom part of unix(7).
*/
iov[0].iov_base = file.name;
iov[0].iov_len = strlen(file.name) + 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = u.buf;
msg.msg_controllen = sizeof(u.buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
fdptr = (int *) CMSG_DATA(cmsg);
memcpy(fdptr, &file.fd, sizeof(int));
return sendmsg(sockfd, &msg, 0);
}
/*
* Receives a file descriptor from the sockfd provided. Returns the file
* descriptor as sent from sendfd(). It will return the file descriptor
* or die (literally) trying. Any synchronisation and preparation of
* state should be done external to this (we expect the other side to be
* in sendfd() in the code).
*/
struct file_t recvfd(int sockfd)
{
struct msghdr msg = {0};
struct iovec iov[1] = {0};
struct cmsghdr *cmsg;
struct file_t file = {0};
int *fdptr;
int olderrno;
union {
char buf[CMSG_SPACE(sizeof(file.fd))];
struct cmsghdr align;
} u;
/* Allocate a buffer. */
/* TODO: Make this dynamic with MSG_PEEK. */
file.name = malloc(TAG_BUFFER);
if (!file.name)
error("recvfd: failed to allocate file.tag buffer\n");
/*
* We need to "recieve" the non-ancillary data even though we don't
* plan to use it at all. Otherwise, things won't work as expected.
* See unix(7) and other well-hidden documentation.
*/
iov[0].iov_base = file.name;
iov[0].iov_len = TAG_BUFFER;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = u.buf;
msg.msg_controllen = sizeof(u.buf);
ssize_t ret = recvmsg(sockfd, &msg, 0);
if (ret < 0)
goto err;
cmsg = CMSG_FIRSTHDR(&msg);
if (!cmsg)
error("recvfd: got NULL from CMSG_FIRSTHDR");
if (cmsg->cmsg_level != SOL_SOCKET)
error("recvfd: expected SOL_SOCKET in cmsg: %d", cmsg->cmsg_level);
if (cmsg->cmsg_type != SCM_RIGHTS)
error("recvfd: expected SCM_RIGHTS in cmsg: %d", cmsg->cmsg_type);
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
error("recvfd: expected correct CMSG_LEN in cmsg: %lu", (unsigned long)cmsg->cmsg_len);
fdptr = (int *) CMSG_DATA(cmsg);
if (!fdptr || *fdptr < 0)
error("recvfd: recieved invalid pointer");
file.fd = *fdptr;
return file;
err:
olderrno = errno;
free(file.name);
errno = olderrno;
return (struct file_t){0};
}

View File

@ -3,7 +3,7 @@
package utils package utils
/* /*
* Copyright 2016 SUSE LLC * Copyright 2016, 2017 SUSE LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,28 +18,66 @@ package utils
* limitations under the License. * limitations under the License.
*/ */
/*
#include <errno.h>
#include <stdlib.h>
#include "cmsg.h"
*/
import "C"
import ( import (
"fmt"
"os" "os"
"unsafe"
"golang.org/x/sys/unix"
) )
// MaxSendfdLen is the maximum length of the name of a file descriptor being
// sent using SendFd. The name of the file handle returned by RecvFd will never
// be larger than this value.
const MaxNameLen = 4096
// oobSpace is the size of the oob slice required to store a single FD. Note
// that unix.UnixRights appears to make the assumption that fd is always int32,
// so sizeof(fd) = 4.
var oobSpace = unix.CmsgSpace(4)
// RecvFd waits for a file descriptor to be sent over the given AF_UNIX // RecvFd waits for a file descriptor to be sent over the given AF_UNIX
// socket. The file name of the remote file descriptor will be recreated // socket. The file name of the remote file descriptor will be recreated
// locally (it is sent as non-auxiliary data in the same payload). // locally (it is sent as non-auxiliary data in the same payload).
func RecvFd(socket *os.File) (*os.File, error) { func RecvFd(socket *os.File) (*os.File, error) {
file, err := C.recvfd(C.int(socket.Fd())) // For some reason, unix.Recvmsg uses the length rather than the capacity
// when passing the msg_controllen and other attributes to recvmsg. So we
// have to actually set the length.
name := make([]byte, MaxNameLen)
oob := make([]byte, oobSpace)
sockfd := socket.Fd()
n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer C.free(unsafe.Pointer(file.name))
return os.NewFile(uintptr(file.fd), C.GoString(file.name)), nil if n >= MaxNameLen || oobn != oobSpace {
return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
}
// Truncate.
name = name[:n]
oob = oob[:oobn]
scms, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return nil, err
}
if len(scms) != 1 {
return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
}
scm := scms[0]
fds, err := unix.ParseUnixRights(&scm)
if err != nil {
return nil, err
}
if len(fds) != 1 {
return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
}
fd := uintptr(fds[0])
return os.NewFile(fd, string(name)), nil
} }
// SendFd sends a file descriptor over the given AF_UNIX socket. In // SendFd sends a file descriptor over the given AF_UNIX socket. In
@ -47,11 +85,11 @@ func RecvFd(socket *os.File) (*os.File, error) {
// non-auxiliary data in the same payload (allowing to send contextual // non-auxiliary data in the same payload (allowing to send contextual
// information for a file descriptor). // information for a file descriptor).
func SendFd(socket, file *os.File) error { func SendFd(socket, file *os.File) error {
var cfile C.struct_file_t name := []byte(file.Name())
cfile.fd = C.int(file.Fd()) if len(name) >= MaxNameLen {
cfile.name = C.CString(file.Name()) return fmt.Errorf("sendfd: filename too long: %s", file.Name())
defer C.free(unsafe.Pointer(cfile.name)) }
oob := unix.UnixRights(int(file.Fd()))
_, err := C.sendfd(C.int(socket.Fd()), cfile) return unix.Sendmsg(int(socket.Fd()), name, oob, nil, 0)
return err
} }

View File

@ -1,36 +0,0 @@
/*
* Copyright 2016 SUSE LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#if !defined(CMSG_H)
#define CMSG_H
#include <sys/types.h>
/* TODO: Implement this properly with MSG_PEEK. */
#define TAG_BUFFER 4096
/* This mirrors Go's (*os.File). */
struct file_t {
char *name;
int fd;
};
struct file_t recvfd(int sockfd);
ssize_t sendfd(int sockfd, struct file_t file);
#endif /* !defined(CMSG_H) */

View File

@ -8,8 +8,9 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"unsafe" "unsafe"
"golang.org/x/sys/unix"
) )
const ( const (
@ -41,7 +42,7 @@ func ResolveRootfs(uncleanRootfs string) (string, error) {
// ExitStatus returns the correct exit status for a process based on if it // ExitStatus returns the correct exit status for a process based on if it
// was signaled or exited cleanly // was signaled or exited cleanly
func ExitStatus(status syscall.WaitStatus) int { func ExitStatus(status unix.WaitStatus) int {
if status.Signaled() { if status.Signaled() {
return exitSignalOffset + int(status.Signal()) return exitSignalOffset + int(status.Signal())
} }

View File

@ -6,7 +6,8 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"strconv" "strconv"
"syscall"
"golang.org/x/sys/unix"
) )
func CloseExecFrom(minFd int) error { func CloseExecFrom(minFd int) error {
@ -26,8 +27,8 @@ func CloseExecFrom(minFd int) error {
continue continue
} }
// intentionally ignore errors from syscall.CloseOnExec // intentionally ignore errors from unix.CloseOnExec
syscall.CloseOnExec(fd) unix.CloseOnExec(fd)
// the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall) // the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall)
} }
return nil return nil
@ -35,7 +36,7 @@ func CloseExecFrom(minFd int) error {
// NewSockPair returns a new unix socket pair // NewSockPair returns a new unix socket pair
func NewSockPair(name string) (parent *os.File, child *os.File, err error) { func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

201
vendor/github.com/opencontainers/selinux/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -52,7 +52,7 @@ func ReserveLabel(label string) error {
return nil return nil
} }
func UnreserveLabel(label string) error { func ReleaseLabel(label string) error {
return nil return nil
} }

View File

@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/opencontainers/runc/libcontainer/selinux" "github.com/opencontainers/selinux/go-selinux"
) )
// Valid Label Options // Valid Label Options
@ -25,10 +25,10 @@ var ErrIncompatibleLabel = fmt.Errorf("Bad SELinux option z and Z can not be use
// the labels. The labels returned will include a random MCS String, that is // the labels. The labels returned will include a random MCS String, that is
// guaranteed to be unique. // guaranteed to be unique.
func InitLabels(options []string) (string, string, error) { func InitLabels(options []string) (string, string, error) {
if !selinux.SelinuxEnabled() { if !selinux.GetEnabled() {
return "", "", nil return "", "", nil
} }
processLabel, mountLabel := selinux.GetLxcContexts() processLabel, mountLabel := selinux.ContainerLabels()
if processLabel != "" { if processLabel != "" {
pcon := selinux.NewContext(processLabel) pcon := selinux.NewContext(processLabel)
mcon := selinux.NewContext(mountLabel) mcon := selinux.NewContext(mountLabel)
@ -55,8 +55,8 @@ func InitLabels(options []string) (string, string, error) {
return processLabel, mountLabel, nil return processLabel, mountLabel, nil
} }
func GetROMountLabel() string { func ROMountLabel() string {
return selinux.GetROFileLabel() return selinux.ROFileLabel()
} }
// DEPRECATED: The GenLabels function is only to be used during the transition to the official API. // DEPRECATED: The GenLabels function is only to be used during the transition to the official API.
@ -88,33 +88,33 @@ func SetProcessLabel(processLabel string) error {
if processLabel == "" { if processLabel == "" {
return nil return nil
} }
return selinux.Setexeccon(processLabel) return selinux.SetExecLabel(processLabel)
} }
// GetProcessLabel returns the process label that the kernel will assign // ProcessLabel returns the process label that the kernel will assign
// to the next program executed by the current process. If "" is returned // to the next program executed by the current process. If "" is returned
// this indicates that the default labeling will happen for the process. // this indicates that the default labeling will happen for the process.
func GetProcessLabel() (string, error) { func ProcessLabel() (string, error) {
return selinux.Getexeccon() return selinux.ExecLabel()
} }
// GetFileLabel returns the label for specified path // GetFileLabel returns the label for specified path
func GetFileLabel(path string) (string, error) { func FileLabel(path string) (string, error) {
return selinux.Getfilecon(path) return selinux.FileLabel(path)
} }
// SetFileLabel modifies the "path" label to the specified file label // SetFileLabel modifies the "path" label to the specified file label
func SetFileLabel(path string, fileLabel string) error { func SetFileLabel(path string, fileLabel string) error {
if selinux.SelinuxEnabled() && fileLabel != "" { if selinux.GetEnabled() && fileLabel != "" {
return selinux.Setfilecon(path, fileLabel) return selinux.SetFileLabel(path, fileLabel)
} }
return nil return nil
} }
// SetFileCreateLabel tells the kernel the label for all files to be created // SetFileCreateLabel tells the kernel the label for all files to be created
func SetFileCreateLabel(fileLabel string) error { func SetFileCreateLabel(fileLabel string) error {
if selinux.SelinuxEnabled() { if selinux.GetEnabled() {
return selinux.Setfscreatecon(fileLabel) return selinux.SetFSCreateLabel(fileLabel)
} }
return nil return nil
} }
@ -123,7 +123,7 @@ func SetFileCreateLabel(fileLabel string) error {
// It changes the MCS label to s0 if shared is true. // It changes the MCS label to s0 if shared is true.
// This will allow all containers to share the content. // This will allow all containers to share the content.
func Relabel(path string, fileLabel string, shared bool) error { func Relabel(path string, fileLabel string, shared bool) error {
if !selinux.SelinuxEnabled() { if !selinux.GetEnabled() {
return nil return nil
} }
@ -147,14 +147,14 @@ func Relabel(path string, fileLabel string, shared bool) error {
return nil return nil
} }
// GetPidLabel will return the label of the process running with the specified pid // PidLabel will return the label of the process running with the specified pid
func GetPidLabel(pid int) (string, error) { func PidLabel(pid int) (string, error) {
return selinux.Getpidcon(pid) return selinux.PidLabel(pid)
} }
// Init initialises the labeling system // Init initialises the labeling system
func Init() { func Init() {
selinux.SelinuxEnabled() selinux.GetEnabled()
} }
// ReserveLabel will record the fact that the MCS label has already been used. // ReserveLabel will record the fact that the MCS label has already been used.
@ -165,11 +165,11 @@ func ReserveLabel(label string) error {
return nil return nil
} }
// UnreserveLabel will remove the reservation of the MCS label. // ReleaseLabel will remove the reservation of the MCS label.
// This will allow InitLabels to use the MCS label in a newly created // This will allow InitLabels to use the MCS label in a newly created
// containers // containers
func UnreserveLabel(label string) error { func ReleaseLabel(label string) error {
selinux.FreeLxcContexts(label) selinux.ReleaseLabel(label)
return nil return nil
} }

View File

@ -15,13 +15,14 @@ import (
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
"github.com/opencontainers/runc/libcontainer/system"
) )
const ( const (
Enforcing = 1 // Enforcing constant indicate SELinux is in enforcing mode
Permissive = 0 Enforcing = 1
// Permissive constant to indicate SELinux is in permissive mode
Permissive = 0
// Disabled constant to indicate SELinux is disabled
Disabled = -1 Disabled = -1
selinuxDir = "/etc/selinux/" selinuxDir = "/etc/selinux/"
selinuxConfig = selinuxDir + "config" selinuxConfig = selinuxDir + "config"
@ -48,7 +49,8 @@ var (
} }
) )
type SELinuxContext map[string]string // Context is a representation of the SELinux label broken into 4 parts
type Context map[string]string
func (s *selinuxState) setEnable(enabled bool) bool { func (s *selinuxState) setEnable(enabled bool) bool {
s.Lock() s.Lock()
@ -69,7 +71,7 @@ func (s *selinuxState) getEnabled() bool {
enabled = false enabled = false
if fs := getSelinuxMountPoint(); fs != "" { if fs := getSelinuxMountPoint(); fs != "" {
if con, _ := Getcon(); con != "kernel" { if con, _ := CurrentLabel(); con != "kernel" {
enabled = true enabled = true
} }
} }
@ -143,8 +145,8 @@ func getSelinuxMountPoint() string {
return state.getSELinuxfs() return state.getSELinuxfs()
} }
// SelinuxEnabled returns whether selinux is currently enabled. // GetEnabled returns whether selinux is currently enabled.
func SelinuxEnabled() bool { func GetEnabled() bool {
return state.getEnabled() return state.getEnabled()
} }
@ -206,43 +208,55 @@ func readCon(name string) (string, error) {
return val, err return val, err
} }
// Setfilecon sets the SELinux label for this path or returns an error. // SetFileLabel sets the SELinux label for this path or returns an error.
func Setfilecon(path string, scon string) error { func SetFileLabel(path string, label string) error {
return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0) return lsetxattr(path, xattrNameSelinux, []byte(label), 0)
} }
// Getfilecon returns the SELinux label for this path or returns an error. // FileLabel returns the SELinux label for this path or returns an error.
func Getfilecon(path string) (string, error) { func FileLabel(path string) (string, error) {
con, err := system.Lgetxattr(path, xattrNameSelinux) label, err := lgetxattr(path, xattrNameSelinux)
if err != nil { if err != nil {
return "", err return "", err
} }
// Trim the NUL byte at the end of the byte buffer, if present. // Trim the NUL byte at the end of the byte buffer, if present.
if len(con) > 0 && con[len(con)-1] == '\x00' { if len(label) > 0 && label[len(label)-1] == '\x00' {
con = con[:len(con)-1] label = label[:len(label)-1]
} }
return string(con), nil return string(label), nil
} }
func Setfscreatecon(scon string) error { /*
return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), scon) SetFSCreateLabel tells kernel the label to create all file system objects
created by this task. Setting label="" to return to default.
*/
func SetFSCreateLabel(label string) error {
return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), label)
} }
func Getfscreatecon() (string, error) { /*
FSCreateLabel returns the default label the kernel which the kernel is using
for file system objects created by this task. "" indicates default.
*/
func FSCreateLabel() (string, error) {
return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid())) return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()))
} }
// Getcon returns the SELinux label of the current process thread, or an error. // CurrentLabel returns the SELinux label of the current process thread, or an error.
func Getcon() (string, error) { func CurrentLabel() (string, error) {
return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid())) return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()))
} }
// Getpidcon returns the SELinux label of the given pid, or an error. // PidLabel returns the SELinux label of the given pid, or an error.
func Getpidcon(pid int) (string, error) { func PidLabel(pid int) (string, error) {
return readCon(fmt.Sprintf("/proc/%d/attr/current", pid)) return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
} }
func Getexeccon() (string, error) { /*
ExecLabel returns the SELinux label that the kernel will use for any programs
that are executed by the current process thread, or an error.
*/
func ExecLabel() (string, error) {
return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid())) return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()))
} }
@ -261,19 +275,25 @@ func writeCon(name string, val string) error {
return err return err
} }
func Setexeccon(scon string) error { /*
return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), scon) SetExecLabel sets the SELinux label that the kernel will use for any programs
that are executed by the current process thread, or an error.
*/
func SetExecLabel(label string) error {
return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), label)
} }
func (c SELinuxContext) Get() string { // Get returns the Context as a string
func (c Context) Get() string {
return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"]) return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
} }
func NewContext(scon string) SELinuxContext { // NewContext creates a new Context struct from the specified label
c := make(SELinuxContext) func NewContext(label string) Context {
c := make(Context)
if len(scon) != 0 { if len(label) != 0 {
con := strings.SplitN(scon, ":", 4) con := strings.SplitN(label, ":", 4)
c["user"] = con[0] c["user"] = con[0]
c["role"] = con[1] c["role"] = con[1]
c["type"] = con[2] c["type"] = con[2]
@ -282,9 +302,10 @@ func NewContext(scon string) SELinuxContext {
return c return c
} }
func ReserveLabel(scon string) { // ReserveLabel reserves the MLS/MCS level component of the specified label
if len(scon) != 0 { func ReserveLabel(label string) {
con := strings.SplitN(scon, ":", 4) if len(label) != 0 {
con := strings.SplitN(label, ":", 4)
mcsAdd(con[3]) mcsAdd(con[3])
} }
} }
@ -293,7 +314,8 @@ func selinuxEnforcePath() string {
return fmt.Sprintf("%s/enforce", selinuxPath) return fmt.Sprintf("%s/enforce", selinuxPath)
} }
func SelinuxGetEnforce() int { // EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
func EnforceMode() int {
var enforce int var enforce int
enforceS, err := readCon(selinuxEnforcePath()) enforceS, err := readCon(selinuxEnforcePath())
@ -308,11 +330,20 @@ func SelinuxGetEnforce() int {
return enforce return enforce
} }
func SelinuxSetEnforce(mode int) error { /*
SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
Disabled is not valid, since this needs to be set at boot time.
*/
func SetEnforceMode(mode int) error {
return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode)) return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode))
} }
func SelinuxGetEnforceMode() int { /*
DefaultEnforceMode returns the systems default SELinux mode Enforcing,
Permissive or Disabled. Note this is is just the default at boot time.
EnforceMode tells you the systems current mode.
*/
func DefaultEnforceMode() int {
switch readConfig(selinuxTag) { switch readConfig(selinuxTag) {
case "enforcing": case "enforcing":
return Enforcing return Enforcing
@ -338,7 +369,7 @@ func mcsDelete(mcs string) {
state.mcsList[mcs] = false state.mcsList[mcs] = false
} }
func IntToMcs(id int, catRange uint32) string { func intToMcs(id int, catRange uint32) string {
var ( var (
SETSIZE = int(catRange) SETSIZE = int(catRange)
TIER = SETSIZE TIER = SETSIZE
@ -386,26 +417,35 @@ func uniqMcs(catRange uint32) string {
return mcs return mcs
} }
func FreeLxcContexts(scon string) { /*
if len(scon) != 0 { ReleaseLabel will unreserve the MLS/MCS Level field of the specified label.
con := strings.SplitN(scon, ":", 4) Allowing it to be used by another process.
*/
func ReleaseLabel(label string) {
if len(label) != 0 {
con := strings.SplitN(label, ":", 4)
mcsDelete(con[3]) mcsDelete(con[3])
} }
} }
var roFileLabel string var roFileLabel string
func GetROFileLabel() (fileLabel string) { // ROFileLabel returns the specified SELinux readonly file label
func ROFileLabel() (fileLabel string) {
return roFileLabel return roFileLabel
} }
func GetLxcContexts() (processLabel string, fileLabel string) { /*
ContainerLabels returns an allocated processLabel and fileLabel to be used for
container labeling by the calling process.
*/
func ContainerLabels() (processLabel string, fileLabel string) {
var ( var (
val, key string val, key string
bufin *bufio.Reader bufin *bufio.Reader
) )
if !SelinuxEnabled() { if !GetEnabled() {
return "", "" return "", ""
} }
lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot()) lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot())
@ -457,7 +497,6 @@ func GetLxcContexts() (processLabel string, fileLabel string) {
roFileLabel = fileLabel roFileLabel = fileLabel
} }
exit: exit:
// mcs := IntToMcs(os.Getpid(), 1024)
mcs := uniqMcs(1024) mcs := uniqMcs(1024)
scon := NewContext(processLabel) scon := NewContext(processLabel)
scon["level"] = mcs scon["level"] = mcs
@ -468,10 +507,15 @@ exit:
return processLabel, fileLabel return processLabel, fileLabel
} }
// SecurityCheckContext validates that the SELinux label is understood by the kernel
func SecurityCheckContext(val string) error { func SecurityCheckContext(val string) error {
return writeCon(fmt.Sprintf("%s.context", selinuxPath), val) return writeCon(fmt.Sprintf("%s.context", selinuxPath), val)
} }
/*
CopyLevel returns a label with the MLS/MCS level from src label replaces on
the dest label.
*/
func CopyLevel(src, dest string) (string, error) { func CopyLevel(src, dest string) (string, error) {
if src == "" { if src == "" {
return "", nil return "", nil
@ -502,25 +546,25 @@ func badPrefix(fpath string) error {
return nil return nil
} }
// Chcon changes the fpath file object to the SELinux label scon. // Chcon changes the fpath file object to the SELinux label label.
// If the fpath is a directory and recurse is true Chcon will walk the // If the fpath is a directory and recurse is true Chcon will walk the
// directory tree setting the label // directory tree setting the label
func Chcon(fpath string, scon string, recurse bool) error { func Chcon(fpath string, label string, recurse bool) error {
if scon == "" { if label == "" {
return nil return nil
} }
if err := badPrefix(fpath); err != nil { if err := badPrefix(fpath); err != nil {
return err return err
} }
callback := func(p string, info os.FileInfo, err error) error { callback := func(p string, info os.FileInfo, err error) error {
return Setfilecon(p, scon) return SetFileLabel(p, label)
} }
if recurse { if recurse {
return filepath.Walk(fpath, callback) return filepath.Walk(fpath, callback)
} }
return Setfilecon(fpath, scon) return SetFileLabel(fpath, label)
} }
// DupSecOpt takes an SELinux process label and returns security options that // DupSecOpt takes an SELinux process label and returns security options that

View File

@ -0,0 +1,78 @@
// +build linux
package selinux
import (
"syscall"
"unsafe"
)
var _zero uintptr
// Returns a []byte slice if the xattr is set and nil otherwise
// Requires path and its attribute as arguments
func lgetxattr(path string, attr string) ([]byte, error) {
var sz int
pathBytes, err := syscall.BytePtrFromString(path)
if err != nil {
return nil, err
}
attrBytes, err := syscall.BytePtrFromString(attr)
if err != nil {
return nil, err
}
// Start with a 128 length byte array
sz = 128
dest := make([]byte, sz)
destBytes := unsafe.Pointer(&dest[0])
_sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
switch {
case errno == syscall.ENODATA:
return nil, errno
case errno == syscall.ENOTSUP:
return nil, errno
case errno == syscall.ERANGE:
// 128 byte array might just not be good enough,
// A dummy buffer is used ``uintptr(0)`` to get real size
// of the xattrs on disk
_sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(unsafe.Pointer(nil)), uintptr(0), 0, 0)
sz = int(_sz)
if sz < 0 {
return nil, errno
}
dest = make([]byte, sz)
destBytes := unsafe.Pointer(&dest[0])
_sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
if errno != 0 {
return nil, errno
}
case errno != 0:
return nil, errno
}
sz = int(_sz)
return dest[:sz], nil
}
func lsetxattr(path string, attr string, data []byte, flags int) error {
pathBytes, err := syscall.BytePtrFromString(path)
if err != nil {
return err
}
attrBytes, err := syscall.BytePtrFromString(attr)
if err != nil {
return err
}
var dataBytes unsafe.Pointer
if len(data) > 0 {
dataBytes = unsafe.Pointer(&data[0])
} else {
dataBytes = unsafe.Pointer(&_zero)
}
_, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0)
if errno != 0 {
return errno
}
return nil
}

24
vendor/github.com/pkg/errors/.gitignore generated vendored Normal file
View File

@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

12
vendor/github.com/pkg/errors/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,12 @@
language: go
go_import_path: github.com/pkg/errors
go:
- 1.4.x
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- tip
script:
- go test -v ./...

23
vendor/github.com/pkg/errors/LICENSE generated vendored Normal file
View File

@ -0,0 +1,23 @@
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Some files were not shown because too many files have changed in this diff Show More