Merge pull request #186 from vishh/fix_godeps
Updating libcontainer dependency via 'godep update'.
This commit is contained in:
commit
a80c3b81a8
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -9,8 +9,8 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/libcontainer",
|
||||
"Comment": "v1.1.0-115-ge6a43c1",
|
||||
"Rev": "e6a43c1c2b9f769deb96348a0a93417cd48a36d8"
|
||||
"Comment": "v1.1.0-194-gedfe81a",
|
||||
"Rev": "edfe81a08b2780ad75b63e60b6cb9eb3a17c671f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||
|
14
Godeps/_workspace/src/github.com/docker/libcontainer/.travis.yml
generated
vendored
14
Godeps/_workspace/src/github.com/docker/libcontainer/.travis.yml
generated
vendored
@ -13,22 +13,24 @@ env:
|
||||
- _GOOS=linux _GOARCH=arm CGO_ENABLED=0
|
||||
|
||||
install:
|
||||
- go get code.google.com/p/go.tools/cmd/cover
|
||||
- mkdir -pv "${GOPATH%%:*}/src/github.com/docker" && [ -d "${GOPATH%%:*}/src/github.com/docker/libcontainer" ] || ln -sv "$(readlink -f .)" "${GOPATH%%:*}/src/github.com/docker/libcontainer"
|
||||
- if [ -z "$TRAVIS_GLOBAL_WTF" ]; then
|
||||
gvm cross "$_GOOS" "$_GOARCH";
|
||||
export GOOS="$_GOOS" GOARCH="$_GOARCH";
|
||||
fi
|
||||
- export GOPATH="$GOPATH:$(pwd)/vendor"
|
||||
- if [ -z "$TRAVIS_GLOBAL_WTF" ]; then go env; fi
|
||||
- go get -d -v ./...
|
||||
- go get -d -v ./... # TODO remove this if /docker/docker gets purged from our includes
|
||||
- if [ "$TRAVIS_GLOBAL_WTF" ]; then
|
||||
export DOCKER_PATH="${GOPATH%%:*}/src/github.com/dotcloud/docker";
|
||||
export DOCKER_PATH="${GOPATH%%:*}/src/github.com/docker/docker";
|
||||
mkdir -p "$DOCKER_PATH/hack/make";
|
||||
( cd "$DOCKER_PATH/hack/make" && wget -c 'https://raw.githubusercontent.com/dotcloud/docker/master/hack/make/'{.validate,validate-dco,validate-gofmt} );
|
||||
sed -i 's!dotcloud/docker!docker/libcontainer!' "$DOCKER_PATH/hack/make/.validate";
|
||||
( cd "$DOCKER_PATH/hack/make" && wget -c 'https://raw.githubusercontent.com/docker/docker/master/hack/make/'{.validate,validate-dco,validate-gofmt} );
|
||||
sed -i 's!docker/docker!docker/libcontainer!' "$DOCKER_PATH/hack/make/.validate";
|
||||
fi
|
||||
|
||||
script:
|
||||
- if [ "$TRAVIS_GLOBAL_WTF" ]; then bash "$DOCKER_PATH/hack/make/validate-dco"; fi
|
||||
- if [ "$TRAVIS_GLOBAL_WTF" ]; then bash "$DOCKER_PATH/hack/make/validate-gofmt"; fi
|
||||
- if [ -z "$TRAVIS_GLOBAL_WTF" ]; then go build -v ./...; fi
|
||||
- if [ -z "$TRAVIS_GLOBAL_WTF" -a "$GOARCH" != 'arm' ]; then go test -test.short -v ./...; fi
|
||||
- if [ -z "$TRAVIS_GLOBAL_WTF" ]; then make direct-build; fi
|
||||
- if [ -z "$TRAVIS_GLOBAL_WTF" -a "$GOARCH" != 'arm' ]; then make direct-test-short; fi
|
||||
|
2
Godeps/_workspace/src/github.com/docker/libcontainer/CONTRIBUTORS_GUIDE.md
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/CONTRIBUTORS_GUIDE.md
generated
vendored
@ -176,7 +176,7 @@ One way to automate this, is customise your get ``commit.template`` by adding
|
||||
a ``prepare-commit-msg`` hook to your libcontainer checkout:
|
||||
|
||||
```
|
||||
curl -o .git/hooks/prepare-commit-msg https://raw.githubusercontent.com/dotcloud/docker/master/contrib/prepare-commit-msg.hook && chmod +x .git/hooks/prepare-commit-msg
|
||||
curl -o .git/hooks/prepare-commit-msg https://raw.githubusercontent.com/docker/docker/master/contrib/prepare-commit-msg.hook && chmod +x .git/hooks/prepare-commit-msg
|
||||
```
|
||||
|
||||
* Note: the above script expects to find your GitHub user name in ``git config --get github.user``
|
||||
|
8
Godeps/_workspace/src/github.com/docker/libcontainer/Dockerfile
generated
vendored
8
Godeps/_workspace/src/github.com/docker/libcontainer/Dockerfile
generated
vendored
@ -1,6 +1,6 @@
|
||||
FROM crosbymichael/golang
|
||||
|
||||
RUN apt-get update && apt-get install -y gcc
|
||||
RUN apt-get update && apt-get install -y gcc make
|
||||
RUN go get code.google.com/p/go.tools/cmd/cover
|
||||
|
||||
# setup a playground for us to spawn containers in
|
||||
@ -14,8 +14,10 @@ COPY . /go/src/github.com/docker/libcontainer
|
||||
WORKDIR /go/src/github.com/docker/libcontainer
|
||||
RUN cp sample_configs/minimal.json /busybox/container.json
|
||||
|
||||
ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
|
||||
|
||||
RUN go get -d -v ./...
|
||||
RUN go install -v ./...
|
||||
RUN make direct-install
|
||||
|
||||
ENTRYPOINT ["/dind"]
|
||||
CMD ["go", "test", "-cover", "./..."]
|
||||
CMD ["make", "direct-test"]
|
||||
|
2
Godeps/_workspace/src/github.com/docker/libcontainer/MAINTAINERS
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/MAINTAINERS
generated
vendored
@ -1,4 +1,6 @@
|
||||
Michael Crosby <michael@docker.com> (@crosbymichael)
|
||||
Rohit Jnagal <jnagal@google.com> (@rjnagal)
|
||||
Victor Marmol <vmarmol@google.com> (@vmarmol)
|
||||
Mrunal Patel <mpatel@redhat.com> (@mrunalp)
|
||||
.travis.yml: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
|
18
Godeps/_workspace/src/github.com/docker/libcontainer/Makefile
generated
vendored
18
Godeps/_workspace/src/github.com/docker/libcontainer/Makefile
generated
vendored
@ -4,7 +4,21 @@ all:
|
||||
|
||||
test:
|
||||
# we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting
|
||||
docker run --rm --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer
|
||||
docker run --rm -it --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer
|
||||
|
||||
sh:
|
||||
docker run --rm -ti -w /busybox --rm --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer nsinit exec sh
|
||||
docker run --rm -it --cap-add NET_ADMIN --cap-add SYS_ADMIN -w /busybox docker/libcontainer nsinit exec sh
|
||||
|
||||
GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u)
|
||||
|
||||
direct-test:
|
||||
go test -cover -v $(GO_PACKAGES)
|
||||
|
||||
direct-test-short:
|
||||
go test -cover -test.short -v $(GO_PACKAGES)
|
||||
|
||||
direct-build:
|
||||
go build -v $(GO_PACKAGES)
|
||||
|
||||
direct-install:
|
||||
go install -v $(GO_PACKAGES)
|
||||
|
2
Godeps/_workspace/src/github.com/docker/libcontainer/README.md
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/README.md
generated
vendored
@ -1,4 +1,4 @@
|
||||
## libcontainer - reference implementation for containers
|
||||
## libcontainer - reference implementation for containers [](https://travis-ci.org/docker/libcontainer)
|
||||
|
||||
### Note on API changes:
|
||||
|
||||
|
1
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/cgroups.go
generated
vendored
1
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/cgroups.go
generated
vendored
@ -37,4 +37,5 @@ type Cgroup struct {
|
||||
|
||||
type ActiveCgroup interface {
|
||||
Cleanup() error
|
||||
Paths() (map[string]string, error)
|
||||
}
|
||||
|
24
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/cgutil/cgutil.go
generated
vendored
24
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/cgutil/cgutil.go
generated
vendored
@ -18,8 +18,8 @@ var createCommand = cli.Command{
|
||||
Name: "create",
|
||||
Usage: "Create a cgroup container using the supplied configuration and initial process.",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"config, c", "cgroup.json", "path to container configuration (cgroups.Cgroup object)"},
|
||||
cli.IntFlag{"pid, p", 0, "pid of the initial process in the container"},
|
||||
cli.StringFlag{Name: "config, c", Value: "cgroup.json", Usage: "path to container configuration (cgroups.Cgroup object)"},
|
||||
cli.IntFlag{Name: "pid, p", Value: 0, Usage: "pid of the initial process in the container"},
|
||||
},
|
||||
Action: createAction,
|
||||
}
|
||||
@ -28,8 +28,8 @@ var destroyCommand = cli.Command{
|
||||
Name: "destroy",
|
||||
Usage: "Destroy an existing cgroup container.",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: destroyAction,
|
||||
}
|
||||
@ -38,8 +38,8 @@ var statsCommand = cli.Command{
|
||||
Name: "stats",
|
||||
Usage: "Get stats for cgroup",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: statsAction,
|
||||
}
|
||||
@ -48,8 +48,8 @@ var pauseCommand = cli.Command{
|
||||
Name: "pause",
|
||||
Usage: "Pause cgroup",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: pauseAction,
|
||||
}
|
||||
@ -58,8 +58,8 @@ var resumeCommand = cli.Command{
|
||||
Name: "resume",
|
||||
Usage: "Resume a paused cgroup",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: resumeAction,
|
||||
}
|
||||
@ -68,8 +68,8 @@ var psCommand = cli.Command{
|
||||
Name: "ps",
|
||||
Usage: "Get list of pids for a cgroup",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: psAction,
|
||||
}
|
||||
|
26
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
26
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
@ -21,12 +21,16 @@ var (
|
||||
"perf_event": &PerfEventGroup{},
|
||||
"freezer": &FreezerGroup{},
|
||||
}
|
||||
CgroupProcesses = "cgroup.procs"
|
||||
)
|
||||
|
||||
type subsystem interface {
|
||||
Set(*data) error
|
||||
// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
|
||||
GetStats(path string, stats *cgroups.Stats) error
|
||||
// Removes the cgroup represented by 'data'.
|
||||
Remove(*data) error
|
||||
GetStats(string, *cgroups.Stats) error
|
||||
// Creates and joins the cgroup represented by data.
|
||||
Set(*data) error
|
||||
}
|
||||
|
||||
type data struct {
|
||||
@ -149,7 +153,23 @@ func (raw *data) parent(subsystem string) (string, error) {
|
||||
return filepath.Join(raw.root, subsystem, initPath), nil
|
||||
}
|
||||
|
||||
func (raw *data) Paths() (map[string]string, error) {
|
||||
paths := make(map[string]string)
|
||||
for sysname := range subsystems {
|
||||
path, err := raw.path(sysname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths[sysname] = path
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (raw *data) path(subsystem string) (string, error) {
|
||||
// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
|
||||
if filepath.IsAbs(raw.cgroup) {
|
||||
return filepath.Join(raw.root, subsystem, raw.cgroup), nil
|
||||
}
|
||||
parent, err := raw.parent(subsystem)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -165,7 +185,7 @@ func (raw *data) join(subsystem string) (string, error) {
|
||||
if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
|
||||
return "", err
|
||||
}
|
||||
if err := writeFile(path, "cgroup.procs", strconv.Itoa(raw.pid)); err != nil {
|
||||
if err := writeFile(path, CgroupProcesses, strconv.Itoa(raw.pid)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
|
4
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go
generated
vendored
4
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go
generated
vendored
@ -54,7 +54,7 @@ func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return err
|
||||
}
|
||||
// sample for 100ms
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
if kernelModeUsage, userModeUsage, err = getCpuUsage(path); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -73,7 +73,7 @@ func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
deltaUsage = lastUsage - startUsage
|
||||
)
|
||||
if deltaSystem > 0.0 {
|
||||
percentage = ((deltaProc / deltaSystem) * clockTicks) * cpuCount
|
||||
percentage = uint64((float64(deltaProc) / float64(deltaSystem)) * float64(clockTicks*cpuCount))
|
||||
}
|
||||
// NOTE: a percentage over 100% is valid for POSIX because that means the
|
||||
// processes is using multiple cores
|
||||
|
31
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/cpuset.go
generated
vendored
31
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/cpuset.go
generated
vendored
@ -20,19 +20,10 @@ func (s *CpusetGroup) Set(d *data) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureParent(dir); err != nil {
|
||||
return err
|
||||
|
||||
return s.SetDir(dir, d.c.CpusetCpus, d.pid)
|
||||
}
|
||||
|
||||
// because we are not using d.join we need to place the pid into the procs file
|
||||
// unlike the other subsystems
|
||||
if err := writeFile(dir, "cgroup.procs", strconv.Itoa(d.pid)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeFile(dir, "cpuset.cpus", d.c.CpusetCpus); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -44,6 +35,24 @@ func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CpusetGroup) SetDir(dir, value string, pid int) error {
|
||||
if err := s.ensureParent(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// because we are not using d.join we need to place the pid into the procs file
|
||||
// unlike the other subsystems
|
||||
if err := writeFile(dir, "cgroup.procs", strconv.Itoa(pid)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeFile(dir, "cpuset.cpus", value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
|
||||
if cpus, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.cpus")); err != nil {
|
||||
return
|
||||
|
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/fs.test
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/fs.test
generated
vendored
Normal file
Binary file not shown.
2
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/memory.go
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/memory.go
generated
vendored
@ -14,7 +14,7 @@ type MemoryGroup struct {
|
||||
|
||||
func (s *MemoryGroup) Set(d *data) error {
|
||||
dir, err := d.join("memory")
|
||||
// only return an error for memory if it was not specified
|
||||
// only return an error for memory if it was specified
|
||||
if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) {
|
||||
return err
|
||||
}
|
||||
|
19
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go
generated
vendored
19
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/utils_test.go
generated
vendored
@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -66,3 +68,20 @@ func TestGetCgroupParamsInt(t *testing.T) {
|
||||
t.Fatal("Expecting error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsolutePathHandling(t *testing.T) {
|
||||
testCgroup := cgroups.Cgroup{
|
||||
Name: "bar",
|
||||
Parent: "/foo",
|
||||
}
|
||||
cgroupData := data{
|
||||
root: "/sys/fs/cgroup",
|
||||
cgroup: "/foo/bar",
|
||||
c: &testCgroup,
|
||||
pid: 1,
|
||||
}
|
||||
expectedPath := filepath.Join(cgroupData.root, "cpu", testCgroup.Parent, testCgroup.Name)
|
||||
if path, err := cgroupData.path("cpu"); path != expectedPath || err != nil {
|
||||
t.Fatalf("expected path %s but got %s %s", expectedPath, path, err)
|
||||
}
|
||||
}
|
||||
|
329
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go
generated
vendored
329
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go
generated
vendored
@ -13,15 +13,14 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
systemd1 "github.com/coreos/go-systemd/dbus"
|
||||
"github.com/docker/docker/pkg/systemd"
|
||||
systemd "github.com/coreos/go-systemd/dbus"
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libcontainer/cgroups/fs"
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
type systemdCgroup struct {
|
||||
cleanupDirs []string
|
||||
cgroup *cgroups.Cgroup
|
||||
}
|
||||
|
||||
type subsystem interface {
|
||||
@ -30,7 +29,7 @@ type subsystem interface {
|
||||
|
||||
var (
|
||||
connLock sync.Mutex
|
||||
theConn *systemd1.Conn
|
||||
theConn *systemd.Conn
|
||||
hasStartTransientUnit bool
|
||||
subsystems = map[string]subsystem{
|
||||
"devices": &fs.DevicesGroup{},
|
||||
@ -45,7 +44,8 @@ var (
|
||||
)
|
||||
|
||||
func UseSystemd() bool {
|
||||
if !systemd.SdBooted() {
|
||||
s, err := os.Stat("/run/systemd/system")
|
||||
if err != nil || !s.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ func UseSystemd() bool {
|
||||
|
||||
if theConn == nil {
|
||||
var err error
|
||||
theConn, err = systemd1.New()
|
||||
theConn, err = systemd.New()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@ -84,267 +84,126 @@ func getIfaceForUnit(unitName string) string {
|
||||
return "Unit"
|
||||
}
|
||||
|
||||
type cgroupArg struct {
|
||||
File string
|
||||
Value string
|
||||
}
|
||||
|
||||
func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
||||
var (
|
||||
unitName = getUnitName(c)
|
||||
slice = "system.slice"
|
||||
properties []systemd1.Property
|
||||
cpuArgs []cgroupArg
|
||||
cpusetArgs []cgroupArg
|
||||
memoryArgs []cgroupArg
|
||||
res systemdCgroup
|
||||
properties []systemd.Property
|
||||
res = &systemdCgroup{}
|
||||
)
|
||||
|
||||
// First set up things not supported by systemd
|
||||
|
||||
// -1 disables memorySwap
|
||||
if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) {
|
||||
memorySwap := c.MemorySwap
|
||||
|
||||
if memorySwap == 0 {
|
||||
// By default, MemorySwap is set to twice the size of RAM.
|
||||
memorySwap = c.Memory * 2
|
||||
}
|
||||
|
||||
memoryArgs = append(memoryArgs, cgroupArg{"memory.memsw.limit_in_bytes", strconv.FormatInt(memorySwap, 10)})
|
||||
}
|
||||
|
||||
if c.CpusetCpus != "" {
|
||||
cpusetArgs = append(cpusetArgs, cgroupArg{"cpuset.cpus", c.CpusetCpus})
|
||||
}
|
||||
res.cgroup = c
|
||||
|
||||
if c.Slice != "" {
|
||||
slice = c.Slice
|
||||
}
|
||||
|
||||
properties = append(properties,
|
||||
systemd1.Property{"Slice", dbus.MakeVariant(slice)},
|
||||
systemd1.Property{"Description", dbus.MakeVariant("docker container " + c.Name)},
|
||||
systemd1.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})},
|
||||
systemd.Property{"Slice", dbus.MakeVariant(slice)},
|
||||
systemd.Property{"Description", dbus.MakeVariant("docker container " + c.Name)},
|
||||
systemd.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})},
|
||||
)
|
||||
|
||||
// Always enable accounting, this gets us the same behaviour as the fs implementation,
|
||||
// plus the kernel has some problems with joining the memory cgroup at a later time.
|
||||
properties = append(properties,
|
||||
systemd1.Property{"MemoryAccounting", dbus.MakeVariant(true)},
|
||||
systemd1.Property{"CPUAccounting", dbus.MakeVariant(true)},
|
||||
systemd1.Property{"BlockIOAccounting", dbus.MakeVariant(true)})
|
||||
systemd.Property{"MemoryAccounting", dbus.MakeVariant(true)},
|
||||
systemd.Property{"CPUAccounting", dbus.MakeVariant(true)},
|
||||
systemd.Property{"BlockIOAccounting", dbus.MakeVariant(true)})
|
||||
|
||||
if c.Memory != 0 {
|
||||
properties = append(properties,
|
||||
systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))})
|
||||
systemd.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))})
|
||||
}
|
||||
// TODO: MemoryReservation and MemorySwap not available in systemd
|
||||
|
||||
if c.CpuShares != 0 {
|
||||
properties = append(properties,
|
||||
systemd1.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))})
|
||||
systemd.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))})
|
||||
}
|
||||
|
||||
if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// To work around the lack of /dev/pts/* support above we need to manually add these
|
||||
// so, ask systemd for the cgroup used
|
||||
props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cgroup := props["ControlGroup"].(string)
|
||||
|
||||
if !c.AllowAllDevices {
|
||||
// Atm we can't use the systemd device support because of two missing things:
|
||||
// * Support for wildcards to allow mknod on any device
|
||||
// * Support for wildcards to allow /dev/pts support
|
||||
//
|
||||
// The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is
|
||||
// in wide use. When both these are availalable we will be able to switch, but need to keep the old
|
||||
// implementation for backwards compat.
|
||||
//
|
||||
// Note: we can't use systemd to set up the initial limits, and then change the cgroup
|
||||
// because systemd will re-write the device settings if it needs to re-apply the cgroup context.
|
||||
// This happens at least for v208 when any sibling unit is started.
|
||||
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint("devices")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
initPath, err := cgroups.GetInitCgroupDir("devices")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir := filepath.Join(mountpoint, initPath, c.Parent, c.Name)
|
||||
|
||||
res.cleanupDirs = append(res.cleanupDirs, dir)
|
||||
|
||||
if err := os.MkdirAll(dir, 0755); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := writeFile(dir, "devices.deny", "a"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, dev := range c.AllowedDevices {
|
||||
if err := writeFile(dir, "devices.allow", dev.GetCgroupAllowString()); err != nil {
|
||||
if err := joinDevices(c, pid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(cpuArgs) != 0 {
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint("cpu")
|
||||
if err != nil {
|
||||
// -1 disables memorySwap
|
||||
if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) {
|
||||
if err := joinMemory(c, pid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := filepath.Join(mountpoint, cgroup)
|
||||
|
||||
for _, arg := range cpuArgs {
|
||||
if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(memoryArgs) != 0 {
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint("memory")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := filepath.Join(mountpoint, cgroup)
|
||||
|
||||
for _, arg := range memoryArgs {
|
||||
if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we need to manually join the freezer cgroup in systemd because it does not currently support it
|
||||
// via the dbus api
|
||||
freezerPath, err := joinFreezer(c, pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.cleanupDirs = append(res.cleanupDirs, freezerPath)
|
||||
|
||||
if len(cpusetArgs) != 0 {
|
||||
// systemd does not atm set up the cpuset controller, so we must manually
|
||||
// join it. Additionally that is a very finicky controller where each
|
||||
// level must have a full setup as the default for a new directory is "no cpus",
|
||||
// so we avoid using any hierarchies here, creating a toplevel directory.
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint("cpuset")
|
||||
if err != nil {
|
||||
if err := joinFreezer(c, pid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
initPath, err := cgroups.GetInitCgroupDir("cpuset")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
foundCpus bool
|
||||
foundMems bool
|
||||
|
||||
rootPath = filepath.Join(mountpoint, initPath)
|
||||
path = filepath.Join(mountpoint, initPath, c.Parent+"-"+c.Name)
|
||||
)
|
||||
|
||||
res.cleanupDirs = append(res.cleanupDirs, path)
|
||||
|
||||
if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, arg := range cpusetArgs {
|
||||
if arg.File == "cpuset.cpus" {
|
||||
foundCpus = true
|
||||
}
|
||||
if arg.File == "cpuset.mems" {
|
||||
foundMems = true
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil {
|
||||
if c.CpusetCpus != "" {
|
||||
if err := joinCpuset(c, pid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// These are required, if not specified inherit from parent
|
||||
if !foundCpus {
|
||||
s, err := ioutil.ReadFile(filepath.Join(rootPath, "cpuset.cpus"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(path, "cpuset.cpus"), s, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// These are required, if not specified inherit from parent
|
||||
if !foundMems {
|
||||
s, err := ioutil.ReadFile(filepath.Join(rootPath, "cpuset.mems"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(path, "cpuset.mems"), s, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func writeFile(dir, file, data string) error {
|
||||
return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
|
||||
}
|
||||
|
||||
func (c *systemdCgroup) Paths() (map[string]string, error) {
|
||||
paths := make(map[string]string)
|
||||
|
||||
for sysname := range subsystems {
|
||||
subsystemPath, err := getSubsystemPath(c.cgroup, sysname)
|
||||
if err != nil {
|
||||
// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
|
||||
if err == cgroups.ErrNotFound {
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paths[sysname] = subsystemPath
|
||||
}
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (c *systemdCgroup) Cleanup() error {
|
||||
// systemd cleans up, we don't need to do much
|
||||
paths, err := c.Paths()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, path := range c.cleanupDirs {
|
||||
for _, path := range paths {
|
||||
os.RemoveAll(path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func joinFreezer(c *cgroups.Cgroup, pid int) (string, error) {
|
||||
func joinFreezer(c *cgroups.Cgroup, pid int) error {
|
||||
path, err := getSubsystemPath(c, "freezer")
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
return ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700)
|
||||
}
|
||||
|
||||
func getSubsystemPath(c *cgroups.Cgroup, subsystem string) (string, error) {
|
||||
@ -389,20 +248,12 @@ func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
|
||||
}
|
||||
|
||||
func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
||||
unitName := getUnitName(c)
|
||||
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint("cpu")
|
||||
path, err := getSubsystemPath(c, "cpu")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cgroup := props["ControlGroup"].(string)
|
||||
|
||||
return cgroups.ReadProcsFile(filepath.Join(mountpoint, cgroup))
|
||||
return cgroups.ReadProcsFile(path)
|
||||
}
|
||||
|
||||
func getUnitName(c *cgroups.Cgroup) string {
|
||||
@ -437,3 +288,71 @@ func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// Atm we can't use the systemd device support because of two missing things:
|
||||
// * Support for wildcards to allow mknod on any device
|
||||
// * Support for wildcards to allow /dev/pts support
|
||||
//
|
||||
// The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is
|
||||
// in wide use. When both these are availalable we will be able to switch, but need to keep the old
|
||||
// implementation for backwards compat.
|
||||
//
|
||||
// Note: we can't use systemd to set up the initial limits, and then change the cgroup
|
||||
// because systemd will re-write the device settings if it needs to re-apply the cgroup context.
|
||||
// This happens at least for v208 when any sibling unit is started.
|
||||
func joinDevices(c *cgroups.Cgroup, pid int) error {
|
||||
path, err := getSubsystemPath(c, "devices")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeFile(path, "devices.deny", "a"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, dev := range c.AllowedDevices {
|
||||
if err := writeFile(path, "devices.allow", dev.GetCgroupAllowString()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func joinMemory(c *cgroups.Cgroup, pid int) error {
|
||||
memorySwap := c.MemorySwap
|
||||
|
||||
if memorySwap == 0 {
|
||||
// By default, MemorySwap is set to twice the size of RAM.
|
||||
memorySwap = c.Memory * 2
|
||||
}
|
||||
|
||||
path, err := getSubsystemPath(c, "memory")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filepath.Join(path, "memory.memsw.limit_in_bytes"), []byte(strconv.FormatInt(memorySwap, 10)), 0700)
|
||||
}
|
||||
|
||||
// systemd does not atm set up the cpuset controller, so we must manually
|
||||
// join it. Additionally that is a very finicky controller where each
|
||||
// level must have a full setup as the default for a new directory is "no cpus"
|
||||
func joinCpuset(c *cgroups.Cgroup, pid int) error {
|
||||
path, err := getSubsystemPath(c, "cpuset")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := &fs.CpusetGroup{}
|
||||
|
||||
return s.SetDir(path, c.CpusetCpus, pid)
|
||||
}
|
||||
|
21
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/utils.go
generated
vendored
21
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/utils.go
generated
vendored
@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -166,3 +167,23 @@ func parseCgroupFile(subsystem string, r io.Reader) (string, error) {
|
||||
}
|
||||
return "", ErrNotFound
|
||||
}
|
||||
|
||||
func pathExists(path string) bool {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func EnterPid(cgroupPaths map[string]string, pid int) error {
|
||||
for _, path := range cgroupPaths {
|
||||
if pathExists(path) {
|
||||
if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"),
|
||||
[]byte(strconv.Itoa(pid)), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
2
Godeps/_workspace/src/github.com/docker/libcontainer/console/console.go
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/console/console.go
generated
vendored
@ -114,7 +114,7 @@ func OpenPtmx() (*os.File, error) {
|
||||
func OpenTerminal(name string, flag int) (*os.File, error) {
|
||||
r, e := syscall.Open(name, flag, 0)
|
||||
if e != nil {
|
||||
return nil, &os.PathError{"open", name, e}
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: e}
|
||||
}
|
||||
return os.NewFile(uintptr(r), name), nil
|
||||
}
|
||||
|
9
Godeps/_workspace/src/github.com/docker/libcontainer/label/label.go
generated
vendored
9
Godeps/_workspace/src/github.com/docker/libcontainer/label/label.go
generated
vendored
@ -2,6 +2,13 @@
|
||||
|
||||
package label
|
||||
|
||||
// InitLabels returns the process label and file labels to be used within
|
||||
// the container. A list of options can be passed into this function to alter
|
||||
// the labels.
|
||||
func InitLabels(options []string) (string, string, error) {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
func GenLabels(options string) (string, string, error) {
|
||||
return "", "", nil
|
||||
}
|
||||
@ -22,7 +29,7 @@ func Relabel(path string, fileLabel string, relabel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetPidCon(pid int) (string, error) {
|
||||
func GetPidLabel(pid int) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
54
Godeps/_workspace/src/github.com/docker/libcontainer/label/label_selinux.go
generated
vendored
54
Godeps/_workspace/src/github.com/docker/libcontainer/label/label_selinux.go
generated
vendored
@ -9,30 +9,49 @@ import (
|
||||
"github.com/docker/libcontainer/selinux"
|
||||
)
|
||||
|
||||
func GenLabels(options string) (string, string, error) {
|
||||
// InitLabels returns the process label and file labels to be used within
|
||||
// the container. A list of options can be passed into this function to alter
|
||||
// the labels. The labels returned will include a random MCS String, that is
|
||||
// guaranteed to be unique.
|
||||
func InitLabels(options []string) (string, string, error) {
|
||||
if !selinux.SelinuxEnabled() {
|
||||
return "", "", nil
|
||||
}
|
||||
var err error
|
||||
processLabel, mountLabel := selinux.GetLxcContexts()
|
||||
if processLabel != "" {
|
||||
var (
|
||||
s = strings.Fields(options)
|
||||
l = len(s)
|
||||
)
|
||||
if l > 0 {
|
||||
pcon := selinux.NewContext(processLabel)
|
||||
for i := 0; i < l; i++ {
|
||||
o := strings.Split(s[i], "=")
|
||||
pcon[o[0]] = o[1]
|
||||
mcon := selinux.NewContext(mountLabel)
|
||||
for _, opt := range options {
|
||||
if opt == "disable" {
|
||||
return "", "", nil
|
||||
}
|
||||
if i := strings.Index(opt, ":"); i == -1 {
|
||||
return "", "", fmt.Errorf("Bad SELinux Option")
|
||||
}
|
||||
con := strings.SplitN(opt, ":", 2)
|
||||
pcon[con[0]] = con[1]
|
||||
if con[0] == "level" || con[0] == "user" {
|
||||
mcon[con[0]] = con[1]
|
||||
}
|
||||
}
|
||||
processLabel = pcon.Get()
|
||||
mountLabel, err = selinux.CopyLevel(processLabel, mountLabel)
|
||||
}
|
||||
mountLabel = mcon.Get()
|
||||
}
|
||||
return processLabel, mountLabel, err
|
||||
}
|
||||
|
||||
// DEPRECATED: The GenLabels function is only to be used during the transition to the official API.
|
||||
func GenLabels(options string) (string, string, error) {
|
||||
return InitLabels(strings.Fields(options))
|
||||
}
|
||||
|
||||
// FormatMountLabel returns a string to be used by the mount command.
|
||||
// The format of this string will be used to alter the labeling of the mountpoint.
|
||||
// The string returned is suitable to be used as the options field of the mount command.
|
||||
// If you need to have additional mount point options, you can pass them in as
|
||||
// the first parameter. Second parameter is the label that you wish to apply
|
||||
// to all content in the mount point.
|
||||
func FormatMountLabel(src, mountLabel string) string {
|
||||
if mountLabel != "" {
|
||||
switch src {
|
||||
@ -45,6 +64,8 @@ func FormatMountLabel(src, mountLabel string) string {
|
||||
return src
|
||||
}
|
||||
|
||||
// SetProcessLabel takes a process label and tells the kernel to assign the
|
||||
// label to the next program executed by the current process.
|
||||
func SetProcessLabel(processLabel string) error {
|
||||
if selinux.SelinuxEnabled() {
|
||||
return selinux.Setexeccon(processLabel)
|
||||
@ -52,6 +73,9 @@ func SetProcessLabel(processLabel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProcessLabel returns the process label that the kernel will assign
|
||||
// to the next program executed by the current process. If "" is returned
|
||||
// this indicates that the default labeling will happen for the process.
|
||||
func GetProcessLabel() (string, error) {
|
||||
if selinux.SelinuxEnabled() {
|
||||
return selinux.Getexeccon()
|
||||
@ -59,6 +83,7 @@ func GetProcessLabel() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// SetFileLabel modifies the "path" label to the specified file label
|
||||
func SetFileLabel(path string, fileLabel string) error {
|
||||
if selinux.SelinuxEnabled() && fileLabel != "" {
|
||||
return selinux.Setfilecon(path, fileLabel)
|
||||
@ -83,17 +108,22 @@ func Relabel(path string, fileLabel string, relabel string) error {
|
||||
return selinux.Chcon(path, fileLabel, true)
|
||||
}
|
||||
|
||||
func GetPidCon(pid int) (string, error) {
|
||||
// GetPidLabel will return the label of the process running with the specified pid
|
||||
func GetPidLabel(pid int) (string, error) {
|
||||
if !selinux.SelinuxEnabled() {
|
||||
return "", nil
|
||||
}
|
||||
return selinux.Getpidcon(pid)
|
||||
}
|
||||
|
||||
// Init initialises the labeling system
|
||||
func Init() {
|
||||
selinux.SelinuxEnabled()
|
||||
}
|
||||
|
||||
// ReserveLabel will record the fact that the MCS label has already been used.
|
||||
// This will prevent InitLabels from using the MCS label in a newly created
|
||||
// container
|
||||
func ReserveLabel(label string) error {
|
||||
selinux.ReserveLabel(label)
|
||||
return nil
|
||||
|
48
Godeps/_workspace/src/github.com/docker/libcontainer/label/label_selinux_test.go
generated
vendored
Normal file
48
Godeps/_workspace/src/github.com/docker/libcontainer/label/label_selinux_test.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
// +build selinux,linux
|
||||
|
||||
package label
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libcontainer/selinux"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
if selinux.SelinuxEnabled() {
|
||||
var testNull []string
|
||||
plabel, mlabel, err := InitLabels(testNull)
|
||||
if err != nil {
|
||||
t.Log("InitLabels Failed")
|
||||
t.Fatal(err)
|
||||
}
|
||||
testDisabled := []string{"disable"}
|
||||
plabel, mlabel, err = InitLabels(testDisabled)
|
||||
if err != nil {
|
||||
t.Log("InitLabels Disabled Failed")
|
||||
t.Fatal(err)
|
||||
}
|
||||
if plabel != "" {
|
||||
t.Log("InitLabels Disabled Failed")
|
||||
t.Fatal()
|
||||
}
|
||||
testUser := []string{"user:user_u", "role:user_r", "type:user_t", "level:s0:c1,c15"}
|
||||
plabel, mlabel, err = InitLabels(testUser)
|
||||
if err != nil {
|
||||
t.Log("InitLabels User Failed")
|
||||
t.Fatal(err)
|
||||
}
|
||||
if plabel != "user_u:user_r:user_t:s0:c1,c15" || mlabel != "user_u:object_r:svirt_sandbox_file_t:s0:c1,c15" {
|
||||
t.Log("InitLabels User Failed")
|
||||
t.Log(plabel, mlabel)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testBadData := []string{"user", "role:user_r", "type:user_t", "level:s0:c1,c15"}
|
||||
plabel, mlabel, err = InitLabels(testBadData)
|
||||
if err == nil {
|
||||
t.Log("InitLabels Bad Failed")
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
2
Godeps/_workspace/src/github.com/docker/libcontainer/mount/init.go
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/mount/init.go
generated
vendored
@ -236,7 +236,7 @@ func reOpenDevNull(rootfs string) error {
|
||||
if stat.Rdev == devNullStat.Rdev {
|
||||
// Close and re-open the fd.
|
||||
if err = syscall.Dup2(int(file.Fd()), fd); err != nil {
|
||||
return fmt.Errorf("Failed to dup fd %d to fd %d - %s", file.Fd(), fd)
|
||||
return fmt.Errorf("Failed to dup fd %d to fd %d - %s", file.Fd(), fd, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/exec.go
generated
vendored
14
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/exec.go
generated
vendored
@ -56,14 +56,19 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||
|
||||
// Do this before syncing with child so that no children
|
||||
// can escape the cgroup
|
||||
cleaner, err := SetupCgroups(container, command.Process.Pid)
|
||||
cgroupRef, err := SetupCgroups(container, command.Process.Pid)
|
||||
if err != nil {
|
||||
command.Process.Kill()
|
||||
command.Wait()
|
||||
return -1, err
|
||||
}
|
||||
if cleaner != nil {
|
||||
defer cleaner.Cleanup()
|
||||
defer cgroupRef.Cleanup()
|
||||
|
||||
cgroupPaths, err := cgroupRef.Paths()
|
||||
if err != nil {
|
||||
command.Process.Kill()
|
||||
command.Wait()
|
||||
return -1, err
|
||||
}
|
||||
|
||||
var networkState network.NetworkState
|
||||
@ -77,6 +82,7 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||
InitPid: command.Process.Pid,
|
||||
InitStartTime: started,
|
||||
NetworkState: networkState,
|
||||
CgroupPaths: cgroupPaths,
|
||||
}
|
||||
|
||||
if err := libcontainer.SaveState(dataPath, state); err != nil {
|
||||
@ -133,7 +139,7 @@ func DefaultCreateCommand(container *libcontainer.Config, console, rootfs, dataP
|
||||
}
|
||||
*/
|
||||
|
||||
command := exec.Command(init, append([]string{"init"}, args...)...)
|
||||
command := exec.Command(init, append([]string{"init", "--"}, args...)...)
|
||||
// make sure the process is executed inside the context of the rootfs
|
||||
command.Dir = rootfs
|
||||
command.Env = append(os.Environ(), env...)
|
||||
|
118
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/execin.go
generated
vendored
118
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/execin.go
generated
vendored
@ -3,26 +3,49 @@
|
||||
package namespaces
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/label"
|
||||
"github.com/docker/libcontainer/system"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libcontainer/label"
|
||||
"github.com/docker/libcontainer/syncpipe"
|
||||
"github.com/docker/libcontainer/system"
|
||||
)
|
||||
|
||||
// Runs the command under 'args' inside an existing container referred to by 'container'.
|
||||
// Returns the exitcode of the command upon success and appropriate error on failure.
|
||||
func RunIn(container *libcontainer.Config, state *libcontainer.State, args []string, nsinitPath string, stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) {
|
||||
initArgs, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, console, args)
|
||||
// ExecIn reexec's the initPath with the argv 0 rewrite to "nsenter" so that it is able to run the
|
||||
// setns code in a single threaded environment joining the existing containers' namespaces.
|
||||
func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath, action string,
|
||||
stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) {
|
||||
|
||||
args := []string{fmt.Sprintf("nsenter-%s", action), "--nspid", strconv.Itoa(state.InitPid)}
|
||||
|
||||
if console != "" {
|
||||
args = append(args, "--console", console)
|
||||
}
|
||||
|
||||
cmd := &exec.Cmd{
|
||||
Path: initPath,
|
||||
Args: append(args, append([]string{"--"}, userArgs...)...),
|
||||
}
|
||||
|
||||
if filepath.Base(initPath) == initPath {
|
||||
if lp, err := exec.LookPath(initPath); err == nil {
|
||||
cmd.Path = lp
|
||||
}
|
||||
}
|
||||
|
||||
pipe, err := syncpipe.NewSyncPipe()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer pipe.Close()
|
||||
|
||||
cmd := exec.Command(nsinitPath, initArgs...)
|
||||
// Note: these are only used in non-tty mode
|
||||
// if there is a tty for the container it will be opened within the namespace and the
|
||||
// fds will be duped to stdin, stdiout, and stderr
|
||||
@ -30,9 +53,24 @@ func RunIn(container *libcontainer.Config, state *libcontainer.State, args []str
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
|
||||
cmd.ExtraFiles = []*os.File{pipe.Child()}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
pipe.CloseChild()
|
||||
|
||||
// Enter cgroups.
|
||||
if err := EnterCgroups(state, cmd.Process.Pid); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if err := pipe.SendToChild(container); err != nil {
|
||||
cmd.Process.Kill()
|
||||
cmd.Wait()
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if startCallback != nil {
|
||||
startCallback(cmd)
|
||||
}
|
||||
@ -46,61 +84,14 @@ func RunIn(container *libcontainer.Config, state *libcontainer.State, args []str
|
||||
return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||
}
|
||||
|
||||
// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
|
||||
func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error {
|
||||
// Enter the namespace and then finish setup
|
||||
args, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, "", args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finalArgs := append([]string{os.Args[0]}, args...)
|
||||
|
||||
if err := system.Execv(finalArgs[0], finalArgs[0:], os.Environ()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func getContainerJson(container *libcontainer.Config) (string, error) {
|
||||
// TODO(vmarmol): If this gets too long, send it over a pipe to the child.
|
||||
// Marshall the container into JSON since it won't be available in the namespace.
|
||||
containerJson, err := json.Marshal(container)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(containerJson), nil
|
||||
}
|
||||
|
||||
func getNsEnterCommand(initPid string, container *libcontainer.Config, console string, args []string) ([]string, error) {
|
||||
containerJson, err := getContainerJson(container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := []string{
|
||||
"nsenter",
|
||||
"--nspid", initPid,
|
||||
"--containerjson", containerJson,
|
||||
}
|
||||
|
||||
if console != "" {
|
||||
out = append(out, "--console", console)
|
||||
}
|
||||
out = append(out, "--")
|
||||
out = append(out, args...)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Run a command in a container after entering the namespace.
|
||||
func NsEnter(container *libcontainer.Config, args []string) error {
|
||||
// clear the current processes env and replace it with the environment
|
||||
// defined on the container
|
||||
// Finalize expects that the setns calls have been setup and that is has joined an
|
||||
// existing namespace
|
||||
func FinalizeSetns(container *libcontainer.Config, args []string) error {
|
||||
// clear the current processes env and replace it with the environment defined on the container
|
||||
if err := LoadContainerEnvironment(container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := FinalizeNamespace(container); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -114,5 +105,10 @@ func NsEnter(container *libcontainer.Config, args []string) error {
|
||||
if err := system.Execv(args[0], args[0:], container.Env); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func EnterCgroups(state *libcontainer.State, pid int) error {
|
||||
return cgroups.EnterPid(state.CgroupPaths, pid)
|
||||
}
|
||||
|
22
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/init.go
generated
vendored
22
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/init.go
generated
vendored
@ -5,11 +5,9 @@ package namespaces
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/docker/pkg/user"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/apparmor"
|
||||
"github.com/docker/libcontainer/console"
|
||||
@ -21,6 +19,7 @@ import (
|
||||
"github.com/docker/libcontainer/security/restrict"
|
||||
"github.com/docker/libcontainer/syncpipe"
|
||||
"github.com/docker/libcontainer/system"
|
||||
"github.com/docker/libcontainer/user"
|
||||
"github.com/docker/libcontainer/utils"
|
||||
)
|
||||
|
||||
@ -28,6 +27,8 @@ import (
|
||||
// Move this to libcontainer package.
|
||||
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
||||
// and other options required for the new container.
|
||||
// The caller of Init function has to ensure that the go runtime is locked to an OS thread
|
||||
// (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended.
|
||||
func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *syncpipe.SyncPipe, args []string) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
@ -47,8 +48,8 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn
|
||||
}
|
||||
|
||||
// We always read this as it is a way to sync with the parent as well
|
||||
networkState, err := syncPipe.ReadFromParent()
|
||||
if err != nil {
|
||||
var networkState *network.NetworkState
|
||||
if err := syncPipe.ReadFromParent(&networkState); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -87,8 +88,6 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn
|
||||
}
|
||||
}
|
||||
|
||||
runtime.LockOSThread()
|
||||
|
||||
if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil {
|
||||
return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err)
|
||||
}
|
||||
@ -119,7 +118,7 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn
|
||||
return fmt.Errorf("restore parent death signal %s", err)
|
||||
}
|
||||
|
||||
return system.Execv(args[0], args[0:], container.Env)
|
||||
return system.Execv(args[0], args[0:], os.Environ())
|
||||
}
|
||||
|
||||
// RestoreParentDeathSignal sets the parent death signal to old.
|
||||
@ -152,7 +151,7 @@ func RestoreParentDeathSignal(old int) error {
|
||||
|
||||
// SetupUser changes the groups, gid, and uid for the user inside the container
|
||||
func SetupUser(u string) error {
|
||||
uid, gid, suppGids, err := user.GetUserGroupSupplementary(u, syscall.Getuid(), syscall.Getgid())
|
||||
uid, gid, suppGids, home, err := user.GetUserGroupSupplementaryHome(u, syscall.Getuid(), syscall.Getgid(), "/")
|
||||
if err != nil {
|
||||
return fmt.Errorf("get supplementary groups %s", err)
|
||||
}
|
||||
@ -169,6 +168,13 @@ func SetupUser(u string) error {
|
||||
return fmt.Errorf("setuid %s", err)
|
||||
}
|
||||
|
||||
// if we didn't get HOME already, set it based on the user's HOME
|
||||
if envHome := os.Getenv("HOME"); envHome == "" {
|
||||
if err := os.Setenv("HOME", home); err != nil {
|
||||
return fmt.Errorf("set HOME %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
223
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter.go
generated
vendored
223
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter.go
generated
vendored
@ -1,223 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package namespaces
|
||||
|
||||
/*
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
static const kBufSize = 256;
|
||||
|
||||
void get_args(int *argc, char ***argv) {
|
||||
// Read argv
|
||||
int fd = open("/proc/self/cmdline", O_RDONLY);
|
||||
|
||||
// Read the whole commandline.
|
||||
ssize_t contents_size = 0;
|
||||
ssize_t contents_offset = 0;
|
||||
char *contents = NULL;
|
||||
ssize_t bytes_read = 0;
|
||||
do {
|
||||
contents_size += kBufSize;
|
||||
contents = (char *) realloc(contents, contents_size);
|
||||
bytes_read = read(fd, contents + contents_offset, contents_size - contents_offset);
|
||||
contents_offset += bytes_read;
|
||||
} while (bytes_read > 0);
|
||||
close(fd);
|
||||
|
||||
// Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args.
|
||||
ssize_t i;
|
||||
*argc = 0;
|
||||
for (i = 0; i < contents_offset; i++) {
|
||||
if (contents[i] == '\0') {
|
||||
(*argc)++;
|
||||
}
|
||||
}
|
||||
*argv = (char **) malloc(sizeof(char *) * ((*argc) + 1));
|
||||
int idx;
|
||||
for (idx = 0; idx < (*argc); idx++) {
|
||||
(*argv)[idx] = contents;
|
||||
contents += strlen(contents) + 1;
|
||||
}
|
||||
(*argv)[*argc] = NULL;
|
||||
}
|
||||
|
||||
// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
|
||||
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
#include "syscall.h"
|
||||
#ifdef SYS_setns
|
||||
int setns(int fd, int nstype) {
|
||||
return syscall(SYS_setns, fd, nstype);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void print_usage() {
|
||||
fprintf(stderr, "<binary> nsenter --nspid <pid> --containerjson <container_json> -- cmd1 arg1 arg2...\n");
|
||||
}
|
||||
|
||||
void nsenter() {
|
||||
int argc;
|
||||
char **argv;
|
||||
get_args(&argc, &argv);
|
||||
|
||||
// Ignore if this is not for us.
|
||||
if (argc < 2 || strcmp(argv[1], "nsenter") != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// USAGE: <binary> nsenter <PID> <process label> <container JSON> <argv>...
|
||||
if (argc < 6) {
|
||||
fprintf(stderr, "nsenter: Incorrect usage, not enough arguments\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static const struct option longopts[] = {
|
||||
{ "nspid", required_argument, NULL, 'n' },
|
||||
{ "containerjson", required_argument, NULL, 'c' },
|
||||
{ "console", required_argument, NULL, 't' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int c;
|
||||
pid_t init_pid = -1;
|
||||
char *init_pid_str = NULL;
|
||||
char *container_json = NULL;
|
||||
char *console = NULL;
|
||||
while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
init_pid_str = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
container_json = optarg;
|
||||
break;
|
||||
case 't':
|
||||
console = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (container_json == NULL || init_pid_str == NULL) {
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
init_pid = strtol(init_pid_str, NULL, 10);
|
||||
if (errno != 0 || init_pid <= 0) {
|
||||
fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with error: \"%s\"\n", init_pid_str, strerror(errno));
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
argc -= 3;
|
||||
argv += 3;
|
||||
|
||||
if (setsid() == -1) {
|
||||
fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// before we setns we need to dup the console
|
||||
int consolefd = -1;
|
||||
if (console != NULL) {
|
||||
consolefd = open(console, O_RDWR);
|
||||
if (consolefd < 0) {
|
||||
fprintf(stderr, "nsenter: failed to open console %s %s\n", console, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Setns on all supported namespaces.
|
||||
char ns_dir[PATH_MAX];
|
||||
memset(ns_dir, 0, PATH_MAX);
|
||||
snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);
|
||||
struct dirent *dent;
|
||||
DIR *dir = opendir(ns_dir);
|
||||
if (dir == NULL) {
|
||||
fprintf(stderr, "nsenter: Failed to open directory \"%s\" with error: \"%s\"\n", ns_dir, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while((dent = readdir(dir)) != NULL) {
|
||||
if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0 || strcmp(dent->d_name, "user") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get and open the namespace for the init we are joining..
|
||||
char buf[PATH_MAX];
|
||||
memset(buf, 0, PATH_MAX);
|
||||
snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, dent->d_name);
|
||||
int fd = open(buf, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, dent->d_name, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set the namespace.
|
||||
if (setns(fd, 0) == -1) {
|
||||
fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", dent->d_name, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
// We must fork to actually enter the PID namespace.
|
||||
int child = fork();
|
||||
if (child == 0) {
|
||||
if (consolefd != -1) {
|
||||
if (dup2(consolefd, STDIN_FILENO) != 0) {
|
||||
fprintf(stderr, "nsenter: failed to dup 0 %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) {
|
||||
fprintf(stderr, "nsenter: failed to dup 1 %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) {
|
||||
fprintf(stderr, "nsenter: failed to dup 2 %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish executing, let the Go runtime take over.
|
||||
return;
|
||||
} else {
|
||||
// Parent, wait for the child.
|
||||
int status = 0;
|
||||
if (waitpid(child, &status, 0) == -1) {
|
||||
fprintf(stderr, "nsenter: Failed to waitpid with error: \"%s\"\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Forward the child's exit code or re-send its death signal.
|
||||
if (WIFEXITED(status)) {
|
||||
exit(WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
kill(getpid(), WTERMSIG(status));
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
__attribute__((constructor)) init() {
|
||||
nsenter();
|
||||
}
|
||||
*/
|
||||
import "C"
|
6
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/README.md
generated
vendored
Normal file
6
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/README.md
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
## nsenter
|
||||
|
||||
The `nsenter` package registers a special init constructor that is called before the Go runtime has
|
||||
a chance to boot. This provides us the ability to `setns` on existing namespaces and avoid the issues
|
||||
that the Go runtime has with multiple threads. This constructor is only called if this package is
|
||||
registered, imported, in your go application and the argv 0 is `nsenter`.
|
218
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c
generated
vendored
Normal file
218
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c
generated
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
// +build cgo
|
||||
//
|
||||
// formated with indent -linux nsenter.c
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
static const kBufSize = 256;
|
||||
static const char *kNsEnter = "nsenter";
|
||||
|
||||
void get_args(int *argc, char ***argv)
|
||||
{
|
||||
// Read argv
|
||||
int fd = open("/proc/self/cmdline", O_RDONLY);
|
||||
|
||||
// Read the whole commandline.
|
||||
ssize_t contents_size = 0;
|
||||
ssize_t contents_offset = 0;
|
||||
char *contents = NULL;
|
||||
ssize_t bytes_read = 0;
|
||||
do {
|
||||
contents_size += kBufSize;
|
||||
contents = (char *)realloc(contents, contents_size);
|
||||
bytes_read =
|
||||
read(fd, contents + contents_offset,
|
||||
contents_size - contents_offset);
|
||||
contents_offset += bytes_read;
|
||||
}
|
||||
while (bytes_read > 0);
|
||||
close(fd);
|
||||
|
||||
// Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args.
|
||||
ssize_t i;
|
||||
*argc = 0;
|
||||
for (i = 0; i < contents_offset; i++) {
|
||||
if (contents[i] == '\0') {
|
||||
(*argc)++;
|
||||
}
|
||||
}
|
||||
*argv = (char **)malloc(sizeof(char *) * ((*argc) + 1));
|
||||
int idx;
|
||||
for (idx = 0; idx < (*argc); idx++) {
|
||||
(*argv)[idx] = contents;
|
||||
contents += strlen(contents) + 1;
|
||||
}
|
||||
(*argv)[*argc] = NULL;
|
||||
}
|
||||
|
||||
// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
|
||||
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
#include "syscall.h"
|
||||
#ifdef SYS_setns
|
||||
int setns(int fd, int nstype)
|
||||
{
|
||||
return syscall(SYS_setns, fd, nstype);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void print_usage()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"nsenter --nspid <pid> --console <console> -- cmd1 arg1 arg2...\n");
|
||||
}
|
||||
|
||||
void nsenter()
|
||||
{
|
||||
int argc, c;
|
||||
char **argv;
|
||||
get_args(&argc, &argv);
|
||||
|
||||
// check argv 0 to ensure that we are supposed to setns
|
||||
// we use strncmp to test for a value of "nsenter" but also allows alternate implmentations
|
||||
// after the setns code path to continue to use the argv 0 to determine actions to be run
|
||||
// resulting in the ability to specify "nsenter-mknod", "nsenter-exec", etc...
|
||||
if (strncmp(argv[0], kNsEnter, strlen(kNsEnter)) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct option longopts[] = {
|
||||
{"nspid", required_argument, NULL, 'n'},
|
||||
{"console", required_argument, NULL, 't'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
pid_t init_pid = -1;
|
||||
char *init_pid_str = NULL;
|
||||
char *console = NULL;
|
||||
while ((c = getopt_long_only(argc, argv, "n:c:", longopts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
init_pid_str = optarg;
|
||||
break;
|
||||
case 't':
|
||||
console = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (init_pid_str == NULL) {
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
init_pid = strtol(init_pid_str, NULL, 10);
|
||||
if ((init_pid == 0 && errno == EINVAL) || errno == ERANGE) {
|
||||
fprintf(stderr,
|
||||
"nsenter: Failed to parse PID from \"%s\" with output \"%d\" and error: \"%s\"\n",
|
||||
init_pid_str, init_pid, strerror(errno));
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
argc -= 3;
|
||||
argv += 3;
|
||||
|
||||
if (setsid() == -1) {
|
||||
fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
// before we setns we need to dup the console
|
||||
int consolefd = -1;
|
||||
if (console != NULL) {
|
||||
consolefd = open(console, O_RDWR);
|
||||
if (consolefd < 0) {
|
||||
fprintf(stderr,
|
||||
"nsenter: failed to open console %s %s\n",
|
||||
console, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// Setns on all supported namespaces.
|
||||
char ns_dir[PATH_MAX];
|
||||
memset(ns_dir, 0, PATH_MAX);
|
||||
snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);
|
||||
|
||||
char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" };
|
||||
const int num = sizeof(namespaces) / sizeof(char *);
|
||||
int i;
|
||||
for (i = 0; i < num; i++) {
|
||||
char buf[PATH_MAX];
|
||||
memset(buf, 0, PATH_MAX);
|
||||
snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]);
|
||||
int fd = open(buf, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
// Ignore nonexistent namespaces.
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
fprintf(stderr,
|
||||
"nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n",
|
||||
buf, namespaces[i], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
// Set the namespace.
|
||||
if (setns(fd, 0) == -1) {
|
||||
fprintf(stderr,
|
||||
"nsenter: Failed to setns for \"%s\" with error: \"%s\"\n",
|
||||
namespaces[i], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// We must fork to actually enter the PID namespace.
|
||||
int child = fork();
|
||||
if (child == 0) {
|
||||
if (consolefd != -1) {
|
||||
if (dup2(consolefd, STDIN_FILENO) != 0) {
|
||||
fprintf(stderr, "nsenter: failed to dup 0 %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) {
|
||||
fprintf(stderr, "nsenter: failed to dup 1 %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) {
|
||||
fprintf(stderr, "nsenter: failed to dup 2 %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// Finish executing, let the Go runtime take over.
|
||||
return;
|
||||
} else {
|
||||
// Parent, wait for the child.
|
||||
int status = 0;
|
||||
if (waitpid(child, &status, 0) == -1) {
|
||||
fprintf(stderr,
|
||||
"nsenter: Failed to waitpid with error: \"%s\"\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
// Forward the child's exit code or re-send its death signal.
|
||||
if (WIFEXITED(status)) {
|
||||
exit(WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
kill(getpid(), WTERMSIG(status));
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
10
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.go
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// +build linux
|
||||
|
||||
package nsenter
|
||||
|
||||
/*
|
||||
__attribute__((constructor)) init() {
|
||||
nsenter();
|
||||
}
|
||||
*/
|
||||
import "C"
|
3
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_unsupported.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
// +build !linux !cgo
|
||||
|
||||
package nsenter
|
7
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go
generated
vendored
7
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go
generated
vendored
@ -21,3 +21,10 @@ type Route struct {
|
||||
Iface *net.Interface
|
||||
Default bool
|
||||
}
|
||||
|
||||
// An IfAddr defines IP network settings for a given network interface
|
||||
type IfAddr struct {
|
||||
Iface *net.Interface
|
||||
IP net.IP
|
||||
IPNet *net.IPNet
|
||||
}
|
||||
|
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.test
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.test
generated
vendored
Normal file
Binary file not shown.
93
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go
generated
vendored
93
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go
generated
vendored
@ -189,13 +189,15 @@ func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
|
||||
}
|
||||
|
||||
func (a *RtAttr) Len() int {
|
||||
if len(a.children) == 0 {
|
||||
return (syscall.SizeofRtAttr + len(a.Data))
|
||||
}
|
||||
|
||||
l := 0
|
||||
for _, child := range a.children {
|
||||
l += child.Len() + syscall.SizeofRtAttr
|
||||
}
|
||||
if l == 0 {
|
||||
l++
|
||||
l += child.Len()
|
||||
}
|
||||
l += syscall.SizeofRtAttr
|
||||
return rtaAlignOf(l + len(a.Data))
|
||||
}
|
||||
|
||||
@ -203,7 +205,7 @@ func (a *RtAttr) ToWireFormat() []byte {
|
||||
native := nativeEndian()
|
||||
|
||||
length := a.Len()
|
||||
buf := make([]byte, rtaAlignOf(length+syscall.SizeofRtAttr))
|
||||
buf := make([]byte, rtaAlignOf(length))
|
||||
|
||||
if a.Data != nil {
|
||||
copy(buf[4:], a.Data)
|
||||
@ -216,11 +218,10 @@ func (a *RtAttr) ToWireFormat() []byte {
|
||||
}
|
||||
}
|
||||
|
||||
if l := uint16(rtaAlignOf(length)); l != 0 {
|
||||
native.PutUint16(buf[0:2], l+1)
|
||||
if l := uint16(length); l != 0 {
|
||||
native.PutUint16(buf[0:2], l)
|
||||
}
|
||||
native.PutUint16(buf[2:4], a.Type)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
@ -650,30 +651,28 @@ func NetworkSetNsFd(iface *net.Interface, fd int) error {
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
// Add an Ip address to an interface. This is identical to:
|
||||
// ip addr add $ip/$ipNet dev $iface
|
||||
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||
func networkLinkIpAction(action, flags int, ifa IfAddr) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
family := getIpFamily(ip)
|
||||
family := getIpFamily(ifa.IP)
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
wb := newNetlinkRequest(action, flags)
|
||||
|
||||
msg := newIfAddrmsg(family)
|
||||
msg.Index = uint32(iface.Index)
|
||||
prefixLen, _ := ipNet.Mask.Size()
|
||||
msg.Index = uint32(ifa.Iface.Index)
|
||||
prefixLen, _ := ifa.IPNet.Mask.Size()
|
||||
msg.Prefixlen = uint8(prefixLen)
|
||||
wb.AddData(msg)
|
||||
|
||||
var ipData []byte
|
||||
if family == syscall.AF_INET {
|
||||
ipData = ip.To4()
|
||||
ipData = ifa.IP.To4()
|
||||
} else {
|
||||
ipData = ip.To16()
|
||||
ipData = ifa.IP.To16()
|
||||
}
|
||||
|
||||
localData := newRtAttr(syscall.IFA_LOCAL, ipData)
|
||||
@ -689,6 +688,26 @@ func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
// Delete an IP address from an interface. This is identical to:
|
||||
// ip addr del $ip/$ipNet dev $iface
|
||||
func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||
return networkLinkIpAction(
|
||||
syscall.RTM_DELADDR,
|
||||
syscall.NLM_F_ACK,
|
||||
IfAddr{iface, ip, ipNet},
|
||||
)
|
||||
}
|
||||
|
||||
// Add an Ip address to an interface. This is identical to:
|
||||
// ip addr add $ip/$ipNet dev $iface
|
||||
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||
return networkLinkIpAction(
|
||||
syscall.RTM_NEWADDR,
|
||||
syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK,
|
||||
IfAddr{iface, ip, ipNet},
|
||||
)
|
||||
}
|
||||
|
||||
func zeroTerminated(s string) []byte {
|
||||
return []byte(s + "\000")
|
||||
}
|
||||
@ -700,6 +719,10 @@ func nonZeroTerminated(s string) []byte {
|
||||
// Add a new network link of a specified type. This is identical to
|
||||
// running: ip add link $name type $linkType
|
||||
func NetworkLinkAdd(name string, linkType string) error {
|
||||
if name == "" || linkType == "" {
|
||||
return fmt.Errorf("Neither link name nor link type can be empty!")
|
||||
}
|
||||
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -711,15 +734,43 @@ func NetworkLinkAdd(name string, linkType string) error {
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
wb.AddData(msg)
|
||||
|
||||
if name != "" {
|
||||
linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||
newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType))
|
||||
wb.AddData(linkInfo)
|
||||
|
||||
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
|
||||
wb.AddData(nameData)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat())
|
||||
wb.AddData(infoData)
|
||||
// Delete a network link. This is identical to
|
||||
// running: ip link del $name
|
||||
func NetworkLinkDel(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("Network link name can not be empty!")
|
||||
}
|
||||
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(iface.Index)
|
||||
wb.AddData(msg)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
|
73
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go
generated
vendored
73
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go
generated
vendored
@ -2,9 +2,55 @@ package netlink
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func ipAssigned(iface *net.Interface, ip net.IP) bool {
|
||||
addrs, _ := iface.Addrs()
|
||||
|
||||
for _, addr := range addrs {
|
||||
args := strings.SplitN(addr.String(), "/", 2)
|
||||
if args[0] == ip.String() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func TestAddDelNetworkIp(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
ifaceName := "lo"
|
||||
ip := net.ParseIP("127.0.1.1")
|
||||
mask := net.IPv4Mask(255, 255, 255, 255)
|
||||
ipNet := &net.IPNet{IP: ip, Mask: mask}
|
||||
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
t.Skip("No 'lo' interface; skipping tests")
|
||||
}
|
||||
|
||||
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !ipAssigned(iface, ip) {
|
||||
t.Fatalf("Could not locate address '%s' in lo address list.", ip.String())
|
||||
}
|
||||
|
||||
if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ipAssigned(iface, ip) {
|
||||
t.Fatalf("Located address '%s' in lo address list after removal.", ip.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateBridgeWithMac(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
@ -27,10 +73,35 @@ func TestCreateBridgeWithMac(t *testing.T) {
|
||||
}
|
||||
|
||||
if _, err := net.InterfaceByName(name); err == nil {
|
||||
t.Fatal("expected error getting interface because bridge was deleted")
|
||||
t.Fatalf("expected error getting interface because %s bridge was deleted", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateBridgeLink(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
name := "mybrlink"
|
||||
|
||||
if err := NetworkLinkAdd(name, "bridge"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := net.InterfaceByName(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := NetworkLinkDel(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := net.InterfaceByName(name); err == nil {
|
||||
t.Fatalf("expected error getting interface because %s bridge was deleted", name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCreateVethPair(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
|
8
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go
generated
vendored
8
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go
generated
vendored
@ -19,6 +19,10 @@ func NetworkLinkAdd(name string, linkType string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func NetworkLinkDel(name string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func NetworkLinkUp(iface *net.Interface) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
@ -27,6 +31,10 @@ func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func AddRoute(destination, source, gateway, device string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
8
Godeps/_workspace/src/github.com/docker/libcontainer/network/network.go
generated
vendored
8
Godeps/_workspace/src/github.com/docker/libcontainer/network/network.go
generated
vendored
@ -44,6 +44,14 @@ func SetInterfaceInNamespacePid(name string, nsPid int) error {
|
||||
return netlink.NetworkSetNsPid(iface, nsPid)
|
||||
}
|
||||
|
||||
func SetInterfaceInNamespaceFd(name string, fd uintptr) error {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.NetworkSetNsFd(iface, int(fd))
|
||||
}
|
||||
|
||||
func SetInterfaceMaster(name, master string) error {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
|
42
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/cli.go
generated
vendored
42
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/cli.go
generated
vendored
@ -1,42 +0,0 @@
|
||||
package nsinit
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var logPath = os.Getenv("log")
|
||||
|
||||
func preload(context *cli.Context) error {
|
||||
if logPath != "" {
|
||||
if err := openLog(logPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NsInit() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "nsinit"
|
||||
app.Version = "0.1"
|
||||
app.Author = "libcontainer maintainers"
|
||||
|
||||
app.Before = preload
|
||||
app.Commands = []cli.Command{
|
||||
execCommand,
|
||||
initCommand,
|
||||
statsCommand,
|
||||
configCommand,
|
||||
nsenterCommand,
|
||||
pauseCommand,
|
||||
unpauseCommand,
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
4
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/config.go
generated
vendored
4
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/config.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -15,7 +15,7 @@ var configCommand = cli.Command{
|
||||
}
|
||||
|
||||
func configAction(context *cli.Context) {
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
42
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/exec.go
generated
vendored
42
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/exec.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
@ -20,12 +21,29 @@ var execCommand = cli.Command{
|
||||
Name: "exec",
|
||||
Usage: "execute a new command inside a container",
|
||||
Action: execAction,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "list", Usage: "list all registered exec functions"},
|
||||
cli.StringFlag{Name: "func", Value: "exec", Usage: "function name to exec inside a container"},
|
||||
},
|
||||
}
|
||||
|
||||
func execAction(context *cli.Context) {
|
||||
if context.Bool("list") {
|
||||
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
|
||||
fmt.Fprint(w, "NAME\tUSAGE\n")
|
||||
|
||||
for k, f := range argvs {
|
||||
fmt.Fprintf(w, "%s\t%s\n", k, f.Usage)
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var exitCode int
|
||||
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -36,7 +54,7 @@ func execAction(context *cli.Context) {
|
||||
}
|
||||
|
||||
if state != nil {
|
||||
exitCode, err = runIn(container, state, []string(context.Args()))
|
||||
exitCode, err = startInExistingContainer(container, state, context.String("func"), context)
|
||||
} else {
|
||||
exitCode, err = startContainer(container, dataPath, []string(context.Args()))
|
||||
}
|
||||
@ -48,28 +66,32 @@ func execAction(context *cli.Context) {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func runIn(container *libcontainer.Config, state *libcontainer.State, args []string) (int, error) {
|
||||
// the process for execing a new process inside an existing container is that we have to exec ourself
|
||||
// with the nsenter argument so that the C code can setns an the namespaces that we require. Then that
|
||||
// code path will drop us into the path that we can do the final setup of the namespace and exec the users
|
||||
// application.
|
||||
func startInExistingContainer(config *libcontainer.Config, state *libcontainer.State, action string, context *cli.Context) (int, error) {
|
||||
var (
|
||||
master *os.File
|
||||
console string
|
||||
err error
|
||||
|
||||
sigc = make(chan os.Signal, 10)
|
||||
|
||||
stdin = os.Stdin
|
||||
stdout = os.Stdout
|
||||
stderr = os.Stderr
|
||||
sigc = make(chan os.Signal, 10)
|
||||
)
|
||||
|
||||
signal.Notify(sigc)
|
||||
|
||||
if container.Tty {
|
||||
if config.Tty {
|
||||
stdin = nil
|
||||
stdout = nil
|
||||
stderr = nil
|
||||
|
||||
master, console, err = consolepkg.CreateMasterAndConsole()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return -1, err
|
||||
}
|
||||
|
||||
go io.Copy(master, os.Stdin)
|
||||
@ -77,7 +99,7 @@ func runIn(container *libcontainer.Config, state *libcontainer.State, args []str
|
||||
|
||||
state, err := term.SetRawTerminal(os.Stdin.Fd())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return -1, err
|
||||
}
|
||||
|
||||
defer term.RestoreTerminal(os.Stdin.Fd(), state)
|
||||
@ -98,7 +120,7 @@ func runIn(container *libcontainer.Config, state *libcontainer.State, args []str
|
||||
}()
|
||||
}
|
||||
|
||||
return namespaces.RunIn(container, state, args, os.Args[0], stdin, stdout, stderr, console, startCallback)
|
||||
return namespaces.ExecIn(config, state, context.Args(), os.Args[0], action, stdin, stdout, stderr, console, startCallback)
|
||||
}
|
||||
|
||||
// startContainer starts the container. Returns the exit status or -1 and an
|
||||
|
7
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/init.go
generated
vendored
7
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/init.go
generated
vendored
@ -1,8 +1,9 @@
|
||||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
@ -23,7 +24,9 @@ var (
|
||||
)
|
||||
|
||||
func initAction(context *cli.Context) {
|
||||
container, err := loadContainer()
|
||||
runtime.LockOSThread()
|
||||
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
67
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/main.go
generated
vendored
Normal file
67
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/main.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
logPath = os.Getenv("log")
|
||||
argvs = make(map[string]*rFunc)
|
||||
)
|
||||
|
||||
func init() {
|
||||
argvs["exec"] = &rFunc{
|
||||
Usage: "execute a process inside an existing container",
|
||||
Action: nsenterExec,
|
||||
}
|
||||
|
||||
argvs["mknod"] = &rFunc{
|
||||
Usage: "mknod a device inside an existing container",
|
||||
Action: nsenterMknod,
|
||||
}
|
||||
|
||||
argvs["ip"] = &rFunc{
|
||||
Usage: "display the container's network interfaces",
|
||||
Action: nsenterIp,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// we need to check our argv 0 for any registred functions to run instead of the
|
||||
// normal cli code path
|
||||
f, exists := argvs[strings.TrimPrefix(os.Args[0], "nsenter-")]
|
||||
if exists {
|
||||
runFunc(f)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Name = "nsinit"
|
||||
app.Version = "0.1"
|
||||
app.Author = "libcontainer maintainers"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "nspid"},
|
||||
cli.StringFlag{Name: "console"},
|
||||
}
|
||||
|
||||
app.Before = preload
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
execCommand,
|
||||
initCommand,
|
||||
statsCommand,
|
||||
configCommand,
|
||||
pauseCommand,
|
||||
unpauseCommand,
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/main/main
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/main/main
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/main/nsinit
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/main/nsinit
generated
vendored
Normal file
Binary file not shown.
105
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/nsenter.go
generated
vendored
105
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/nsenter.go
generated
vendored
@ -1,41 +1,84 @@
|
||||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/devices"
|
||||
"github.com/docker/libcontainer/mount/nodes"
|
||||
"github.com/docker/libcontainer/namespaces"
|
||||
_ "github.com/docker/libcontainer/namespaces/nsenter"
|
||||
)
|
||||
|
||||
var nsenterCommand = cli.Command{
|
||||
Name: "nsenter",
|
||||
Usage: "init process for entering an existing namespace",
|
||||
Action: nsenterAction,
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "nspid"},
|
||||
cli.StringFlag{Name: "containerjson"},
|
||||
cli.StringFlag{Name: "console"},
|
||||
},
|
||||
}
|
||||
|
||||
func nsenterAction(context *cli.Context) {
|
||||
args := context.Args()
|
||||
|
||||
if len(args) == 0 {
|
||||
args = []string{"/bin/bash"}
|
||||
}
|
||||
|
||||
container, err := loadContainerFromJson(context.String("containerjson"))
|
||||
if err != nil {
|
||||
log.Fatalf("unable to load container: %s", err)
|
||||
}
|
||||
|
||||
nspid := context.Int("nspid")
|
||||
if nspid <= 0 {
|
||||
log.Fatalf("cannot enter into namespaces without valid pid: %q", nspid)
|
||||
}
|
||||
|
||||
if err := namespaces.NsEnter(container, args); err != nil {
|
||||
// nsenterExec exec's a process inside an existing container
|
||||
func nsenterExec(config *libcontainer.Config, args []string) {
|
||||
if err := namespaces.FinalizeSetns(config, args); err != nil {
|
||||
log.Fatalf("failed to nsenter: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// nsenterMknod runs mknod inside an existing container
|
||||
//
|
||||
// mknod <path> <type> <major> <minor>
|
||||
func nsenterMknod(config *libcontainer.Config, args []string) {
|
||||
if len(args) != 4 {
|
||||
log.Fatalf("expected mknod to have 4 arguments not %d", len(args))
|
||||
}
|
||||
|
||||
t := rune(args[1][0])
|
||||
|
||||
major, err := strconv.Atoi(args[2])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
minor, err := strconv.Atoi(args[3])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
n := &devices.Device{
|
||||
Path: args[0],
|
||||
Type: t,
|
||||
MajorNumber: int64(major),
|
||||
MinorNumber: int64(minor),
|
||||
}
|
||||
|
||||
if err := nodes.CreateDeviceNode("/", n); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// nsenterIp displays the network interfaces inside a container's net namespace
|
||||
func nsenterIp(config *libcontainer.Config, args []string) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
|
||||
fmt.Fprint(w, "NAME\tMTU\tMAC\tFLAG\tADDRS\n")
|
||||
|
||||
for _, iface := range interfaces {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
o := []string{}
|
||||
|
||||
for _, a := range addrs {
|
||||
o = append(o, a.String())
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\n", iface.Name, iface.MTU, iface.HardwareAddr, iface.Flags, strings.Join(o, ","))
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
}
|
||||
|
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/nsinit
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/nsinit
generated
vendored
Normal file
Binary file not shown.
7
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/nsinit/nsinit.go
generated
vendored
7
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/nsinit/nsinit.go
generated
vendored
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "github.com/docker/libcontainer/nsinit"
|
||||
|
||||
func main() {
|
||||
nsinit.NsInit()
|
||||
}
|
4
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/pause.go
generated
vendored
4
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/pause.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
@ -34,7 +34,7 @@ func unpauseAction(context *cli.Context) {
|
||||
}
|
||||
|
||||
func toggle(state cgroups.FreezerState) error {
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
4
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/stats.go
generated
vendored
4
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/stats.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -16,7 +16,7 @@ var statsCommand = cli.Command{
|
||||
}
|
||||
|
||||
func statsAction(context *cli.Context) {
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
60
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/utils.go
generated
vendored
60
Godeps/_workspace/src/github.com/docker/libcontainer/nsinit/utils.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -6,10 +6,18 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/syncpipe"
|
||||
)
|
||||
|
||||
func loadContainer() (*libcontainer.Config, error) {
|
||||
// rFunc is a function registration for calling after an execin
|
||||
type rFunc struct {
|
||||
Usage string
|
||||
Action func(*libcontainer.Config, []string)
|
||||
}
|
||||
|
||||
func loadConfig() (*libcontainer.Config, error) {
|
||||
f, err := os.Open(filepath.Join(dataPath, "container.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -35,12 +43,52 @@ func openLog(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadContainerFromJson(rawData string) (*libcontainer.Config, error) {
|
||||
var container *libcontainer.Config
|
||||
func findUserArgs() []string {
|
||||
i := 0
|
||||
for _, a := range os.Args {
|
||||
i++
|
||||
|
||||
if err := json.Unmarshal([]byte(rawData), &container); err != nil {
|
||||
if a == "--" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return os.Args[i:]
|
||||
}
|
||||
|
||||
// loadConfigFromFd loads a container's config from the sync pipe that is provided by
|
||||
// fd 3 when running a process
|
||||
func loadConfigFromFd() (*libcontainer.Config, error) {
|
||||
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return container, nil
|
||||
var config *libcontainer.Config
|
||||
if err := syncPipe.ReadFromParent(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func preload(context *cli.Context) error {
|
||||
if logPath != "" {
|
||||
if err := openLog(logPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runFunc(f *rFunc) {
|
||||
userArgs := findUserArgs()
|
||||
|
||||
config, err := loadConfigFromFd()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to receive config from sync pipe: %s", err)
|
||||
}
|
||||
|
||||
f.Action(config, userArgs)
|
||||
}
|
||||
|
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/nsinitb
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/nsinitb
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/security/capabilities/capabilities.test
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/docker/libcontainer/security/capabilities/capabilities.test
generated
vendored
Normal file
Binary file not shown.
3
Godeps/_workspace/src/github.com/docker/libcontainer/state.go
generated
vendored
3
Godeps/_workspace/src/github.com/docker/libcontainer/state.go
generated
vendored
@ -18,6 +18,9 @@ type State struct {
|
||||
|
||||
// Network runtime state.
|
||||
NetworkState network.NetworkState `json:"network_state,omitempty"`
|
||||
|
||||
// Path to all the cgroups setup for a container. Key is cgroup subsystem name.
|
||||
CgroupPaths map[string]string `json:"cgroup_paths,omitempty"`
|
||||
}
|
||||
|
||||
// The running state of the container.
|
||||
|
19
Godeps/_workspace/src/github.com/docker/libcontainer/syncpipe/sync_pipe.go
generated
vendored
19
Godeps/_workspace/src/github.com/docker/libcontainer/syncpipe/sync_pipe.go
generated
vendored
@ -6,8 +6,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/libcontainer/network"
|
||||
)
|
||||
|
||||
// SyncPipe allows communication to and from the child processes
|
||||
@ -39,8 +37,8 @@ func (s *SyncPipe) Parent() *os.File {
|
||||
return s.parent
|
||||
}
|
||||
|
||||
func (s *SyncPipe) SendToChild(networkState *network.NetworkState) error {
|
||||
data, err := json.Marshal(networkState)
|
||||
func (s *SyncPipe) SendToChild(v interface{}) error {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -63,18 +61,19 @@ func (s *SyncPipe) ReadFromChild() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SyncPipe) ReadFromParent() (*network.NetworkState, error) {
|
||||
func (s *SyncPipe) ReadFromParent(v interface{}) error {
|
||||
data, err := ioutil.ReadAll(s.child)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading from sync pipe %s", err)
|
||||
return fmt.Errorf("error reading from sync pipe %s", err)
|
||||
}
|
||||
var networkState *network.NetworkState
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &networkState); err != nil {
|
||||
return nil, err
|
||||
if err := json.Unmarshal(data, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return networkState, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SyncPipe) ReportChildError(err error) {
|
||||
|
16
Godeps/_workspace/src/github.com/docker/libcontainer/syncpipe/sync_pipe_test.go
generated
vendored
16
Godeps/_workspace/src/github.com/docker/libcontainer/syncpipe/sync_pipe_test.go
generated
vendored
@ -3,10 +3,12 @@ package syncpipe
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libcontainer/network"
|
||||
)
|
||||
|
||||
type testStruct struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func TestSendErrorFromChild(t *testing.T) {
|
||||
pipe, err := NewSyncPipe()
|
||||
if err != nil {
|
||||
@ -46,16 +48,16 @@ func TestSendPayloadToChild(t *testing.T) {
|
||||
|
||||
expected := "libcontainer"
|
||||
|
||||
if err := pipe.SendToChild(&network.NetworkState{VethHost: expected}); err != nil {
|
||||
if err := pipe.SendToChild(testStruct{Name: expected}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
payload, err := pipe.ReadFromParent()
|
||||
if err != nil {
|
||||
var s *testStruct
|
||||
if err := pipe.ReadFromParent(&s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if payload.VethHost != expected {
|
||||
t.Fatalf("expected veth host %q but received %q", expected, payload.VethHost)
|
||||
if s.Name != expected {
|
||||
t.Fatalf("expected name %q but received %q", expected, s.Name)
|
||||
}
|
||||
}
|
||||
|
2
Godeps/_workspace/src/github.com/docker/libcontainer/system/sysconfig.go
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/system/sysconfig.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
// +build linux,cgo
|
||||
// +build cgo
|
||||
|
||||
package system
|
||||
|
||||
|
2
Godeps/_workspace/src/github.com/docker/libcontainer/system/sysconfig_notcgo.go
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/system/sysconfig_notcgo.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
// +build linux,!cgo
|
||||
// +build !cgo
|
||||
|
||||
package system
|
||||
|
||||
|
18
Godeps/_workspace/src/github.com/docker/libcontainer/test/utils.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/docker/libcontainer/test/utils.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package libcontainer
|
||||
|
||||
import (
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/security/capabilities"
|
||||
)
|
||||
|
||||
func GetAllCapabilities() []string {
|
||||
return capabilities.GetAllCapabilities()
|
||||
}
|
||||
|
||||
func DropBoundingSet(container *libcontainer.Config) error {
|
||||
return capabilities.DropBoundingSet(container.Capabilities)
|
||||
}
|
||||
|
||||
func DropCapabilities(container *libcontainer.Config) error {
|
||||
return capabilities.DropCapabilities(container.Capabilities)
|
||||
}
|
18
Godeps/_workspace/src/github.com/docker/libcontainer/test/utils.go~
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/docker/libcontainer/test/utils.go~
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package libcontainer
|
||||
|
||||
import (
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/security/capabilities"
|
||||
)
|
||||
|
||||
func GetAllCapabilities() []string {
|
||||
return capabilities.GetAllCapabilities()
|
||||
}
|
||||
|
||||
func DropBoundingSet(container *libcontainer.Container) error {
|
||||
return capabilities.DropBoundingSet(container.Capabilities)
|
||||
}
|
||||
|
||||
func DropCapabilities(container *libcontainer.Container) error {
|
||||
return capabilities.DropCapabilities(container.Capabilities)
|
||||
}
|
1
Godeps/_workspace/src/github.com/docker/libcontainer/user/MAINTAINERS
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/docker/libcontainer/user/MAINTAINERS
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
258
Godeps/_workspace/src/github.com/docker/libcontainer/user/user.go
generated
vendored
Normal file
258
Godeps/_workspace/src/github.com/docker/libcontainer/user/user.go
generated
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
minId = 0
|
||||
maxId = 1<<31 - 1 //for 32-bit systems compatibility
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRange = fmt.Errorf("Uids and gids must be in range %d-%d", minId, maxId)
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Pass string
|
||||
Uid int
|
||||
Gid int
|
||||
Gecos string
|
||||
Home string
|
||||
Shell string
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
Name string
|
||||
Pass string
|
||||
Gid int
|
||||
List []string
|
||||
}
|
||||
|
||||
func parseLine(line string, v ...interface{}) {
|
||||
if line == "" {
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.Split(line, ":")
|
||||
for i, p := range parts {
|
||||
if len(v) <= i {
|
||||
// if we have more "parts" than we have places to put them, bail for great "tolerance" of naughty configuration files
|
||||
break
|
||||
}
|
||||
|
||||
switch e := v[i].(type) {
|
||||
case *string:
|
||||
// "root", "adm", "/bin/bash"
|
||||
*e = p
|
||||
case *int:
|
||||
// "0", "4", "1000"
|
||||
// ignore string to int conversion errors, for great "tolerance" of naughty configuration files
|
||||
*e, _ = strconv.Atoi(p)
|
||||
case *[]string:
|
||||
// "", "root", "root,adm,daemon"
|
||||
if p != "" {
|
||||
*e = strings.Split(p, ",")
|
||||
} else {
|
||||
*e = []string{}
|
||||
}
|
||||
default:
|
||||
// panic, because this is a programming/logic error, not a runtime one
|
||||
panic("parseLine expects only pointers! argument " + strconv.Itoa(i) + " is not a pointer!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ParsePasswd() ([]*User, error) {
|
||||
return ParsePasswdFilter(nil)
|
||||
}
|
||||
|
||||
func ParsePasswdFilter(filter func(*User) bool) ([]*User, error) {
|
||||
f, err := os.Open("/etc/passwd")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return parsePasswdFile(f, filter)
|
||||
}
|
||||
|
||||
func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []*User{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
text := strings.TrimSpace(s.Text())
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// see: man 5 passwd
|
||||
// name:password:UID:GID:GECOS:directory:shell
|
||||
// Name:Pass:Uid:Gid:Gecos:Home:Shell
|
||||
// root:x:0:0:root:/root:/bin/bash
|
||||
// adm:x:3:4:adm:/var/adm:/bin/false
|
||||
p := &User{}
|
||||
parseLine(
|
||||
text,
|
||||
&p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell,
|
||||
)
|
||||
|
||||
if filter == nil || filter(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func ParseGroup() ([]*Group, error) {
|
||||
return ParseGroupFilter(nil)
|
||||
}
|
||||
|
||||
func ParseGroupFilter(filter func(*Group) bool) ([]*Group, error) {
|
||||
f, err := os.Open("/etc/group")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return parseGroupFile(f, filter)
|
||||
}
|
||||
|
||||
func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []*Group{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
text := s.Text()
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// see: man 5 group
|
||||
// group_name:password:GID:user_list
|
||||
// Name:Pass:Gid:List
|
||||
// root:x:0:root
|
||||
// adm:x:4:root,adm,daemon
|
||||
p := &Group{}
|
||||
parseLine(
|
||||
text,
|
||||
&p.Name, &p.Pass, &p.Gid, &p.List,
|
||||
)
|
||||
|
||||
if filter == nil || filter(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Given a string like "user", "1000", "user:group", "1000:1000", returns the uid, gid, list of supplementary group IDs, and home directory, if available and/or applicable.
|
||||
func GetUserGroupSupplementaryHome(userSpec string, defaultUid, defaultGid int, defaultHome string) (int, int, []int, string, error) {
|
||||
var (
|
||||
uid = defaultUid
|
||||
gid = defaultGid
|
||||
suppGids = []int{}
|
||||
home = defaultHome
|
||||
|
||||
userArg, groupArg string
|
||||
)
|
||||
|
||||
// allow for userArg to have either "user" syntax, or optionally "user:group" syntax
|
||||
parseLine(userSpec, &userArg, &groupArg)
|
||||
|
||||
users, err := ParsePasswdFilter(func(u *User) bool {
|
||||
if userArg == "" {
|
||||
return u.Uid == uid
|
||||
}
|
||||
return u.Name == userArg || strconv.Itoa(u.Uid) == userArg
|
||||
})
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if userArg == "" {
|
||||
userArg = strconv.Itoa(uid)
|
||||
}
|
||||
return 0, 0, nil, "", fmt.Errorf("Unable to find user %v: %v", userArg, err)
|
||||
}
|
||||
|
||||
haveUser := users != nil && len(users) > 0
|
||||
if haveUser {
|
||||
// if we found any user entries that matched our filter, let's take the first one as "correct"
|
||||
uid = users[0].Uid
|
||||
gid = users[0].Gid
|
||||
home = users[0].Home
|
||||
} else if userArg != "" {
|
||||
// we asked for a user but didn't find them... let's check to see if we wanted a numeric user
|
||||
uid, err = strconv.Atoi(userArg)
|
||||
if err != nil {
|
||||
// not numeric - we have to bail
|
||||
return 0, 0, nil, "", fmt.Errorf("Unable to find user %v", userArg)
|
||||
}
|
||||
if uid < minId || uid > maxId {
|
||||
return 0, 0, nil, "", ErrRange
|
||||
}
|
||||
|
||||
// if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit
|
||||
}
|
||||
|
||||
if groupArg != "" || (haveUser && users[0].Name != "") {
|
||||
groups, err := ParseGroupFilter(func(g *Group) bool {
|
||||
if groupArg != "" {
|
||||
return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg
|
||||
}
|
||||
for _, u := range g.List {
|
||||
if u == users[0].Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return 0, 0, nil, "", fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
|
||||
}
|
||||
|
||||
haveGroup := groups != nil && len(groups) > 0
|
||||
if groupArg != "" {
|
||||
if haveGroup {
|
||||
// if we found any group entries that matched our filter, let's take the first one as "correct"
|
||||
gid = groups[0].Gid
|
||||
} else {
|
||||
// we asked for a group but didn't find id... let's check to see if we wanted a numeric group
|
||||
gid, err = strconv.Atoi(groupArg)
|
||||
if err != nil {
|
||||
// not numeric - we have to bail
|
||||
return 0, 0, nil, "", fmt.Errorf("Unable to find group %v", groupArg)
|
||||
}
|
||||
if gid < minId || gid > maxId {
|
||||
return 0, 0, nil, "", ErrRange
|
||||
}
|
||||
|
||||
// if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit
|
||||
}
|
||||
} else if haveGroup {
|
||||
suppGids = make([]int, len(groups))
|
||||
for i, group := range groups {
|
||||
suppGids[i] = group.Gid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uid, gid, suppGids, home, nil
|
||||
}
|
94
Godeps/_workspace/src/github.com/docker/libcontainer/user/user_test.go
generated
vendored
Normal file
94
Godeps/_workspace/src/github.com/docker/libcontainer/user/user_test.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUserParseLine(t *testing.T) {
|
||||
var (
|
||||
a, b string
|
||||
c []string
|
||||
d int
|
||||
)
|
||||
|
||||
parseLine("", &a, &b)
|
||||
if a != "" || b != "" {
|
||||
t.Fatalf("a and b should be empty ('%v', '%v')", a, b)
|
||||
}
|
||||
|
||||
parseLine("a", &a, &b)
|
||||
if a != "a" || b != "" {
|
||||
t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b)
|
||||
}
|
||||
|
||||
parseLine("bad boys:corny cows", &a, &b)
|
||||
if a != "bad boys" || b != "corny cows" {
|
||||
t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b)
|
||||
}
|
||||
|
||||
parseLine("", &c)
|
||||
if len(c) != 0 {
|
||||
t.Fatalf("c should be empty (%#v)", c)
|
||||
}
|
||||
|
||||
parseLine("d,e,f:g:h:i,j,k", &c, &a, &b, &c)
|
||||
if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" {
|
||||
t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c)
|
||||
}
|
||||
|
||||
parseLine("::::::::::", &a, &b, &c)
|
||||
if a != "" || b != "" || len(c) != 0 {
|
||||
t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c)
|
||||
}
|
||||
|
||||
parseLine("not a number", &d)
|
||||
if d != 0 {
|
||||
t.Fatalf("d should be 0 (%v)", d)
|
||||
}
|
||||
|
||||
parseLine("b:12:c", &a, &d, &b)
|
||||
if a != "b" || b != "c" || d != 12 {
|
||||
t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserParsePasswd(t *testing.T) {
|
||||
users, err := parsePasswdFile(strings.NewReader(`
|
||||
root:x:0:0:root:/root:/bin/bash
|
||||
adm:x:3:4:adm:/var/adm:/bin/false
|
||||
this is just some garbage data
|
||||
`), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(users) != 3 {
|
||||
t.Fatalf("Expected 3 users, got %v", len(users))
|
||||
}
|
||||
if users[0].Uid != 0 || users[0].Name != "root" {
|
||||
t.Fatalf("Expected users[0] to be 0 - root, got %v - %v", users[0].Uid, users[0].Name)
|
||||
}
|
||||
if users[1].Uid != 3 || users[1].Name != "adm" {
|
||||
t.Fatalf("Expected users[1] to be 3 - adm, got %v - %v", users[1].Uid, users[1].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserParseGroup(t *testing.T) {
|
||||
groups, err := parseGroupFile(strings.NewReader(`
|
||||
root:x:0:root
|
||||
adm:x:4:root,adm,daemon
|
||||
this is just some garbage data
|
||||
`), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(groups) != 3 {
|
||||
t.Fatalf("Expected 3 groups, got %v", len(groups))
|
||||
}
|
||||
if groups[0].Gid != 0 || groups[0].Name != "root" || len(groups[0].List) != 1 {
|
||||
t.Fatalf("Expected groups[0] to be 0 - root - 1 member, got %v - %v - %v", groups[0].Gid, groups[0].Name, len(groups[0].List))
|
||||
}
|
||||
if groups[1].Gid != 4 || groups[1].Name != "adm" || len(groups[1].List) != 3 {
|
||||
t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List))
|
||||
}
|
||||
}
|
@ -4,4 +4,4 @@ set -e
|
||||
set -x
|
||||
|
||||
# Statically build cAdvisor from source and stage it.
|
||||
godep go build --ldflags '-extldflags "-static"' github.com/google/cadvisor
|
||||
godep go build -a --ldflags '-extldflags "-static"' github.com/google/cadvisor
|
||||
|
Loading…
Reference in New Issue
Block a user