From 5fc19e8db53f0b4c473f7e31bfa77e7e1c5d157b Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 3 Feb 2015 17:44:58 -0800 Subject: [PATCH] Rename Fs fields to fs Signed-off-by: Michael Crosby --- PRINCIPLES.md | 2 +- cgroups/fs/apply_raw.go | 2 +- configs/config.go | 14 +- configs/config_test.go | 68 +++++++++- configs/device.go | 24 +++- configs/mount.go | 4 +- configs/network.go | 6 +- devices/devices.go | 6 +- integration/exec_test.go | 16 +-- integration/template_test.go | 4 +- integration/utils_test.go | 6 +- linux_container.go | 128 +----------------- linux_factory.go | 119 +++++++++++++++- linux_factory_test.go | 6 +- mount/init.go | 24 ++-- mount/ptmx.go | 4 +- process.go | 15 +- sample_configs/apparmor.json | 2 +- sample_configs/attach_to_bridge.json | 2 +- sample_configs/host-pid.json | 2 +- sample_configs/minimal.json | 2 +- .../route_source_address_selection.json | 2 +- sample_configs/selinux.json | 2 +- sample_configs/userns.json | 2 +- 24 files changed, 266 insertions(+), 196 deletions(-) diff --git a/PRINCIPLES.md b/PRINCIPLES.md index 42396c0e..05606421 100644 --- a/PRINCIPLES.md +++ b/PRINCIPLES.md @@ -8,7 +8,7 @@ In the design and development of libcontainer we try to follow these principles: * Less code is better. * Fewer components are better. Do you really need to add one more class? * 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand. -* Don't do later what you can do now. "//FIXME: refactor" is not acceptable in new code. +* Don't do later what you can do now. "//TODO: refactor" is not acceptable in new code. * When hesitating between two options, choose the one that is easier to reverse. * "No" is temporary; "Yes" is forever. If you're not sure about a new feature, say no. You can change your mind later. * Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable. diff --git a/cgroups/fs/apply_raw.go b/cgroups/fs/apply_raw.go index 4a3a8864..a56b59a1 100644 --- a/cgroups/fs/apply_raw.go +++ b/cgroups/fs/apply_raw.go @@ -83,7 +83,7 @@ func (m *Manager) Apply(pid int) error { if err := sys.Set(d); err != nil { return err } - // FIXME: Apply should, ideally, be reentrant or be broken up into a separate + // TODO: Apply should, ideally, be reentrant or be broken up into a separate // create and join phase so that the cgroup hierarchy for a container can be // created then join consists of writing the process pids to cgroup.procs p, err := d.path(name) diff --git a/configs/config.go b/configs/config.go index 844a9cad..0f939ade 100644 --- a/configs/config.go +++ b/configs/config.go @@ -26,22 +26,22 @@ type Config struct { // This is required when using read only root filesystems. In these cases, a read/writeable path can be (bind) mounted somewhere inside the root filesystem to act as pivot. PivotDir string `json:"pivot_dir,omitempty"` - // ReadonlyFs will remount the container's rootfs as readonly where only externally mounted - // bind mounts are writtable - ReadonlyFs bool `json:"readonly_fs,omitempty"` + // Path to a directory containing the container's root filesystem. + Rootfs string `json:"rootfs,omitempty"` + + // Readonlyfs will remount the container's rootfs as readonly where only externally mounted + // bind mounts are writtable. + Readonlyfs bool `json:"readonlyfs,omitempty"` // Mounts specify additional source and destination paths that will be mounted inside the container's // rootfs and mount namespace if specified Mounts []*Mount `json:"mounts,omitempty"` // The device nodes that should be automatically created within the container upon container start. Note, make sure that the node is marked as allowed in the cgroup as well! - DeviceNodes []*Device `json:"device_nodes,omitempty"` + Devices []*Device `json:"devices,omitempty"` MountLabel string `json:"mount_label,omitempty"` - // Pathname to container's root filesystem - RootFs string `json:"root_fs,omitempty"` - // Hostname optionally sets the container's hostname if provided Hostname string `json:"hostname,omitempty"` diff --git a/configs/config_test.go b/configs/config_test.go index a74ccd40..2128d020 100644 --- a/configs/config_test.go +++ b/configs/config_test.go @@ -114,7 +114,7 @@ func TestConfigJsonFormat(t *testing.T) { } for _, d := range DefaultSimpleDevices { - if !containsDevice(d, container.DeviceNodes) { + if !containsDevice(d, container.Devices) { t.Logf("expected device configuration for %s", d.Path) t.Fail() } @@ -163,3 +163,69 @@ func TestRemoveNamespace(t *testing.T) { t.Fatalf("namespaces should have 0 items but reports %d", len(ns)) } } + +func TestHostUIDNoUSERNS(t *testing.T) { + config := &Config{ + Namespaces: Namespaces{}, + } + uid, err := config.HostUID() + if err != nil { + t.Fatal(err) + } + if uid != 0 { + t.Fatal("expected uid 0 with no USERNS but received %d", uid) + } +} + +func TestHostUIDWithUSERNS(t *testing.T) { + config := &Config{ + Namespaces: Namespaces{{Type: NEWUSER}}, + UidMappings: []IDMap{ + { + ContainerID: 0, + HostID: 1000, + Size: 1, + }, + }, + } + uid, err := config.HostUID() + if err != nil { + t.Fatal(err) + } + if uid != 1000 { + t.Fatal("expected uid 1000 with no USERNS but received %d", uid) + } +} + +func TestHostGIDNoUSERNS(t *testing.T) { + config := &Config{ + Namespaces: Namespaces{}, + } + uid, err := config.HostGID() + if err != nil { + t.Fatal(err) + } + if uid != 0 { + t.Fatal("expected gid 0 with no USERNS but received %d", uid) + } +} + +func TestHostGIDWithUSERNS(t *testing.T) { + config := &Config{ + Namespaces: Namespaces{{Type: NEWUSER}}, + GidMappings: []IDMap{ + { + ContainerID: 0, + HostID: 1000, + Size: 1, + }, + }, + } + uid, err := config.HostGID() + if err != nil { + t.Fatal(err) + } + if uid != 1000 { + t.Fatal("expected gid 1000 with no USERNS but received %d", uid) + } +} diff --git a/configs/device.go b/configs/device.go index 18d73232..a8117068 100644 --- a/configs/device.go +++ b/configs/device.go @@ -10,19 +10,29 @@ const ( ) type Device struct { + // Device type, block, char, etc. Type rune `json:"type,omitempty"` - // It is fine if this is an empty string in the case that you are using Wildcards + + // Path to the device. Path string `json:"path,omitempty"` - // Use the wildcard constant for wildcards. + + // Major is the device's major number. Major int64 `json:"major,omitempty"` - // Use the wildcard constant for wildcards. + + // Minor is the device's minor number. Minor int64 `json:"minor,omitempty"` - // Typically just "rwm" + + // Cgroup permissions format, rwm. Permissions string `json:"permissions,omitempty"` - // The permission bits of the file's mode + + // FileMode permission bits for the device. FileMode os.FileMode `json:"file_mode,omitempty"` - Uid uint32 `json:"uid,omitempty"` - Gid uint32 `json:"gid,omitempty"` + + // Uid of the device. + Uid uint32 `json:"uid,omitempty"` + + // Gid of the device. + Gid uint32 `json:"gid,omitempty"` } func (d *Device) CgroupString() string { diff --git a/configs/mount.go b/configs/mount.go index f6f39992..5a26a287 100644 --- a/configs/mount.go +++ b/configs/mount.go @@ -52,7 +52,7 @@ func (m *Mount) bindMount(rootfs, mountLabel string) error { return err } - // FIXME: (crosbymichael) This does not belong here and should be done a layer above + // TODO: (crosbymichael) This does not belong here and should be done a layer above dest, err = symlink.FollowSymlinkInScope(dest, rootfs) if err != nil { return err @@ -94,7 +94,7 @@ func (m *Mount) tmpfsMount(rootfs, mountLabel string) error { dest = filepath.Join(rootfs, m.Destination) ) - // FIXME: (crosbymichael) This does not belong here and should be done a layer above + // TODO: (crosbymichael) This does not belong here and should be done a layer above if dest, err = symlink.FollowSymlinkInScope(dest, rootfs); err != nil { return err } diff --git a/configs/network.go b/configs/network.go index 54218363..edcc4dd4 100644 --- a/configs/network.go +++ b/configs/network.go @@ -20,12 +20,12 @@ type Network struct { // Address contains the IPv4 and mask to set on the network interface Address string `json:"address,omitempty"` - // IPv6Address contains the IPv6 and mask to set on the network interface - IPv6Address string `json:"ipv6_address,omitempty"` - // Gateway sets the gateway address that is used as the default for the interface Gateway string `json:"gateway,omitempty"` + // IPv6Address contains the IPv6 and mask to set on the network interface + IPv6Address string `json:"ipv6_address,omitempty"` + // IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface IPv6Gateway string `json:"ipv6_gateway,omitempty"` diff --git a/devices/devices.go b/devices/devices.go index b3f67aa3..537f71af 100644 --- a/devices/devices.go +++ b/devices/devices.go @@ -60,10 +60,10 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) { } func HostDevices() ([]*configs.Device, error) { - return getDeviceNodes("/dev") + return getDevices("/dev") } -func getDeviceNodes(path string) ([]*configs.Device, error) { +func getDevices(path string) ([]*configs.Device, error) { files, err := ioutilReadDir(path) if err != nil { return nil, err @@ -76,7 +76,7 @@ func getDeviceNodes(path string) ([]*configs.Device, error) { case "pts", "shm", "fd", "mqueue": continue default: - sub, err := getDeviceNodes(filepath.Join(path, f.Name())) + sub, err := getDevices(filepath.Join(path, f.Name())) if err != nil { return nil, err } diff --git a/integration/exec_test.go b/integration/exec_test.go index 855613de..0f93957c 100644 --- a/integration/exec_test.go +++ b/integration/exec_test.go @@ -27,7 +27,7 @@ func testExecPS(t *testing.T, userns bool) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -65,7 +65,7 @@ func TestIPCPrivate(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -96,7 +96,7 @@ func TestIPCHost(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -128,7 +128,7 @@ func TestIPCJoinPath(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -161,7 +161,7 @@ func TestIPCBadPath(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -181,7 +181,7 @@ func TestRlimit(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -232,7 +232,7 @@ func TestEnter(t *testing.T) { } defer os.RemoveAll(root) - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -335,7 +335,7 @@ func TestFreeze(t *testing.T) { } defer os.RemoveAll(root) - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } diff --git a/integration/template_test.go b/integration/template_test.go index 28c80196..28160c98 100644 --- a/integration/template_test.go +++ b/integration/template_test.go @@ -12,7 +12,7 @@ import ( // and the default setup for devices func newTemplateConfig(rootfs string) *configs.Config { return &configs.Config{ - RootFs: rootfs, + Rootfs: rootfs, Capabilities: []string{ "CHOWN", "DAC_OVERRIDE", @@ -43,7 +43,7 @@ func newTemplateConfig(rootfs string) *configs.Config { AllowedDevices: configs.DefaultAllowedDevices, }, - DeviceNodes: configs.DefaultAutoCreatedDevices, + Devices: configs.DefaultAutoCreatedDevices, Hostname: "integration", Env: []string{ "HOME=/root", diff --git a/integration/utils_test.go b/integration/utils_test.go index 39c9a126..75cba52b 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -29,7 +29,7 @@ type stdBuffers struct { } func writeConfig(config *configs.Config) error { - f, err := os.OpenFile(filepath.Join(config.RootFs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) + f, err := os.OpenFile(filepath.Join(config.Rootfs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) if err != nil { return err } @@ -51,8 +51,8 @@ func loadConfig() (*configs.Config, error) { return container, nil } -// newRootFs creates a new tmp directory and copies the busybox root filesystem -func newRootFs() (string, error) { +// newRootfs creates a new tmp directory and copies the busybox root filesystem +func newRootfs() (string, error) { dir, err := ioutil.TempDir("", "") if err != nil { return "", err diff --git a/linux_container.go b/linux_container.go index bf501c86..bf6b6a09 100644 --- a/linux_container.go +++ b/linux_container.go @@ -6,17 +6,13 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "os/exec" "path/filepath" "syscall" - "github.com/docker/libcontainer/apparmor" "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/configs" - "github.com/docker/libcontainer/label" - "github.com/docker/libcontainer/mount" "github.com/docker/libcontainer/network" "github.com/docker/libcontainer/system" "github.com/golang/glog" @@ -106,7 +102,7 @@ func (c *linuxContainer) Start(process *Process) (int, error) { cmd.Stdout = process.Stdout cmd.Stderr = process.Stderr cmd.Env = c.config.Env - cmd.Dir = c.config.RootFs + cmd.Dir = c.config.Rootfs if cmd.SysProcAttr == nil { cmd.SysProcAttr = &syscall.SysProcAttr{} } @@ -161,7 +157,7 @@ func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, args []string) (int, err return -1, terr } // Enter cgroups. - if err := enterCgroups(c.state, pid.Pid); err != nil { + if err := c.enterCgroups(pid.Pid); err != nil { return terminate(err) } encoder := json.NewEncoder(parent) @@ -398,7 +394,7 @@ func executeSetupCmd(args []string, ppid int, container *configs.Config, process } defer parent.Close() command.ExtraFiles = []*os.File{child} - command.Dir = container.RootFs + command.Dir = container.Rootfs command.Env = append(command.Env, fmt.Sprintf("_LIBCONTAINER_INITPID=%d", ppid), fmt.Sprintf("_LIBCONTAINER_USERNS=1")) @@ -460,120 +456,6 @@ type pid struct { Pid int `json:"Pid"` } -// Finalize entering into a container and execute a specified command -func InitIn(pipe *os.File) (err error) { - defer func() { - // if we have an error during the initialization of the container's init then send it back to the - // parent process in the form of an initError. - if err != nil { - // ensure that any data sent from the parent is consumed so it doesn't - // receive ECONNRESET when the child writes to the pipe. - ioutil.ReadAll(pipe) - if err := json.NewEncoder(pipe).Encode(initError{ - Message: err.Error(), - }); err != nil { - panic(err) - } - } - // ensure that this pipe is always closed - pipe.Close() - }() - decoder := json.NewDecoder(pipe) - var config *configs.Config - if err := decoder.Decode(&config); err != nil { - return err - } - var process *processArgs - if err := decoder.Decode(&process); err != nil { - return err - } - if err := finalizeSetns(config); err != nil { - return err - } - if err := system.Execv(process.Args[0], process.Args[0:], config.Env); err != nil { - return err - } - panic("unreachable") -} - -// finalize expects that the setns calls have been setup and that is has joined an -// existing namespace -func finalizeSetns(container *configs.Config) 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 := setupRlimits(container); err != nil { - return fmt.Errorf("setup rlimits %s", err) - } - - if err := finalizeNamespace(container); err != nil { - return err - } - - if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil { - return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err) - } - - if container.ProcessLabel != "" { - if err := label.SetProcessLabel(container.ProcessLabel); err != nil { - return err - } - } - - return nil -} - -// SetupContainer is run to setup mounts and networking related operations -// for a user namespace enabled process as a user namespace root doesn't -// have permissions to perform these operations. -// The setup process joins all the namespaces of user namespace enabled init -// except the user namespace, so it run as root in the root user namespace -// to perform these operations. -func SetupContainer(process *processArgs) error { - container := process.Config - networkState := process.NetworkState - - // TODO : move to validation - /* - rootfs, err := utils.ResolveRootfs(container.RootFs) - if err != nil { - return err - } - */ - - // clear the current processes env and replace it with the environment - // defined on the container - if err := loadContainerEnvironment(container); err != nil { - return err - } - - cloneFlags := container.Namespaces.CloneFlags() - if (cloneFlags & syscall.CLONE_NEWNET) == 0 { - if len(container.Networks) != 0 || len(container.Routes) != 0 { - return fmt.Errorf("unable to apply network parameters without network namespace") - } - } else { - if err := setupNetwork(container, networkState); err != nil { - return fmt.Errorf("setup networking %s", err) - } - if err := setupRoute(container); err != nil { - return fmt.Errorf("setup route %s", err) - } - } - - label.Init() - - // InitializeMountNamespace() can be executed only for a new mount namespace - if (cloneFlags & syscall.CLONE_NEWNS) != 0 { - if err := mount.InitializeMountNamespace(container); err != nil { - return fmt.Errorf("setup mount namespace %s", err) - } - } - return nil -} - -func enterCgroups(state *configs.State, pid int) error { - return cgroups.EnterPid(state.CgroupPaths, pid) +func (c *linuxContainer) enterCgroups(pid int) error { + return cgroups.EnterPid(c.state.CgroupPaths, pid) } diff --git a/linux_factory.go b/linux_factory.go index d7bf8a50..02456c12 100644 --- a/linux_factory.go +++ b/linux_factory.go @@ -141,7 +141,7 @@ func (l *linuxFactory) StartInitialization(pipefd uintptr) (err error) { setupUserns := os.Getenv("_LIBCONTAINER_USERNS") != "" pid := os.Getenv("_LIBCONTAINER_INITPID") if pid != "" && !setupUserns { - return InitIn(pipe) + return initIn(pipe) } defer func() { // if we have an error during the initialization of the container's init then send it back to the @@ -169,7 +169,7 @@ func (l *linuxFactory) StartInitialization(pipefd uintptr) (err error) { return err } if setupUserns { - err = SetupContainer(process) + err = setupContainer(process) if err == nil { os.Exit(0) } else { @@ -348,7 +348,6 @@ func (l *linuxFactory) initUserNs(uncleanRootfs string, process *processArgs) (e if config.WorkingDir == "" { config.WorkingDir = "/" } - if err := setupRlimits(config); err != nil { return fmt.Errorf("setup rlimits %s", err) } @@ -557,3 +556,117 @@ func joinExistingNamespaces(namespaces []configs.Namespace) error { } return nil } + +// setupContainer is run to setup mounts and networking related operations +// for a user namespace enabled process as a user namespace root doesn't +// have permissions to perform these operations. +// The setup process joins all the namespaces of user namespace enabled init +// except the user namespace, so it run as root in the root user namespace +// to perform these operations. +func setupContainer(process *processArgs) error { + container := process.Config + networkState := process.NetworkState + + // TODO : move to validation + /* + rootfs, err := utils.ResolveRootfs(container.Rootfs) + if err != nil { + return err + } + */ + + // clear the current processes env and replace it with the environment + // defined on the container + if err := loadContainerEnvironment(container); err != nil { + return err + } + + cloneFlags := container.Namespaces.CloneFlags() + if (cloneFlags & syscall.CLONE_NEWNET) == 0 { + if len(container.Networks) != 0 || len(container.Routes) != 0 { + return fmt.Errorf("unable to apply network parameters without network namespace") + } + } else { + if err := setupNetwork(container, networkState); err != nil { + return fmt.Errorf("setup networking %s", err) + } + if err := setupRoute(container); err != nil { + return fmt.Errorf("setup route %s", err) + } + } + + label.Init() + + // InitializeMountNamespace() can be executed only for a new mount namespace + if (cloneFlags & syscall.CLONE_NEWNS) != 0 { + if err := mount.InitializeMountNamespace(container); err != nil { + return fmt.Errorf("setup mount namespace %s", err) + } + } + return nil +} + +// Finalize entering into a container and execute a specified command +func initIn(pipe *os.File) (err error) { + defer func() { + // if we have an error during the initialization of the container's init then send it back to the + // parent process in the form of an initError. + if err != nil { + // ensure that any data sent from the parent is consumed so it doesn't + // receive ECONNRESET when the child writes to the pipe. + ioutil.ReadAll(pipe) + if err := json.NewEncoder(pipe).Encode(initError{ + Message: err.Error(), + }); err != nil { + panic(err) + } + } + // ensure that this pipe is always closed + pipe.Close() + }() + decoder := json.NewDecoder(pipe) + var config *configs.Config + if err := decoder.Decode(&config); err != nil { + return err + } + var process *processArgs + if err := decoder.Decode(&process); err != nil { + return err + } + if err := finalizeSetns(config); err != nil { + return err + } + if err := system.Execv(process.Args[0], process.Args[0:], config.Env); err != nil { + return err + } + panic("unreachable") +} + +// finalize expects that the setns calls have been setup and that is has joined an +// existing namespace +func finalizeSetns(container *configs.Config) 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 := setupRlimits(container); err != nil { + return fmt.Errorf("setup rlimits %s", err) + } + + if err := finalizeNamespace(container); err != nil { + return err + } + + if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil { + return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err) + } + + if container.ProcessLabel != "" { + if err := label.SetProcessLabel(container.ProcessLabel); err != nil { + return err + } + } + + return nil +} diff --git a/linux_factory_test.go b/linux_factory_test.go index fd1e830a..a5f0b7e1 100644 --- a/linux_factory_test.go +++ b/linux_factory_test.go @@ -86,7 +86,7 @@ func TestFactoryLoadContainer(t *testing.T) { var ( id = "1" expectedConfig = &configs.Config{ - RootFs: "/mycontainer/root", + Rootfs: "/mycontainer/root", } expectedState = &configs.State{ InitPid: 1024, @@ -119,8 +119,8 @@ func TestFactoryLoadContainer(t *testing.T) { config := container.Config() - if config.RootFs != expectedConfig.RootFs { - t.Fatalf("expected rootfs %q but received %q", expectedConfig.RootFs, config.RootFs) + if config.Rootfs != expectedConfig.Rootfs { + t.Fatalf("expected rootfs %q but received %q", expectedConfig.Rootfs, config.Rootfs) } lcontainer, ok := container.(*linuxContainer) diff --git a/mount/init.go b/mount/init.go index 646970d5..a017ea51 100644 --- a/mount/init.go +++ b/mount/init.go @@ -33,11 +33,11 @@ func InitializeMountNamespace(config *configs.Config) (err error) { } // apply any user specified mounts within the new mount namespace for _, m := range config.Mounts { - if err := m.Mount(config.RootFs, config.MountLabel); err != nil { + if err := m.Mount(config.Rootfs, config.MountLabel); err != nil { return err } } - if err := createDeviceNodes(config); err != nil { + if err := createDevices(config); err != nil { return err } if err := setupPtmx(config); err != nil { @@ -51,21 +51,21 @@ func InitializeMountNamespace(config *configs.Config) (err error) { return err } } - if err := setupDevSymlinks(config.RootFs); err != nil { + if err := setupDevSymlinks(config.Rootfs); err != nil { return err } - if err := syscall.Chdir(config.RootFs); err != nil { + if err := syscall.Chdir(config.Rootfs); err != nil { return err } if config.NoPivotRoot { - err = msMoveRoot(config.RootFs) + err = msMoveRoot(config.Rootfs) } else { - err = pivotRoot(config.RootFs, config.PivotDir) + err = pivotRoot(config.Rootfs, config.PivotDir) } if err != nil { return err } - if config.ReadonlyFs { + if config.Readonlyfs { if err := setReadonly(); err != nil { return fmt.Errorf("set readonly %s", err) } @@ -77,7 +77,7 @@ func InitializeMountNamespace(config *configs.Config) (err error) { // mountSystem sets up linux specific system mounts like mqueue, sys, proc, shm, and devpts // inside the mount namespace func mountSystem(config *configs.Config) error { - for _, m := range newSystemMounts(config.RootFs, config.MountLabel, config.RestrictSys) { + for _, m := range newSystemMounts(config.Rootfs, config.MountLabel, config.RestrictSys) { if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) { return fmt.Errorf("mkdirall %s %s", m.path, err) } @@ -164,10 +164,10 @@ func reOpenDevNull(rootfs string) error { } // Create the device nodes in the container. -func createDeviceNodes(config *configs.Config) error { +func createDevices(config *configs.Config) error { oldMask := syscall.Umask(0000) - for _, node := range config.DeviceNodes { - if err := createDeviceNode(config.RootFs, node); err != nil { + for _, node := range config.Devices { + if err := createDeviceNode(config.Rootfs, node); err != nil { syscall.Umask(oldMask) return err } @@ -211,5 +211,5 @@ func prepareRoot(config *configs.Config) error { if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { return err } - return syscall.Mount(config.RootFs, config.RootFs, "bind", syscall.MS_BIND|syscall.MS_REC, "") + return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "") } diff --git a/mount/ptmx.go b/mount/ptmx.go index 657318dc..278f7299 100644 --- a/mount/ptmx.go +++ b/mount/ptmx.go @@ -12,7 +12,7 @@ import ( ) func setupPtmx(config *configs.Config) error { - ptmx := filepath.Join(config.RootFs, "dev/ptmx") + ptmx := filepath.Join(config.Rootfs, "dev/ptmx") if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { return err } @@ -28,7 +28,7 @@ func setupPtmx(config *configs.Config) error { if err != nil { return err } - return console.Setup(config.RootFs, config.Console, config.MountLabel, uid, gid) + return console.Setup(config.Rootfs, config.Console, config.MountLabel, uid, gid) } return nil } diff --git a/process.go b/process.go index ed228f79..c26b8bf5 100644 --- a/process.go +++ b/process.go @@ -2,19 +2,18 @@ package libcontainer import "io" -// Configuration for a process to be run inside a container. +// Process specifies the configuration and IO for a process inside +// a container. type Process struct { // The command to be run followed by any arguments. Args []string + // Stdin is a pointer to a reader which provides the standard input stream. + Stdin io.Reader + // Stdout is a pointer to a writer which receives the standard output stream. - // Stderr is a pointer to a writer which receives the standard error stream. - // - // If a reader or writer is nil, the input stream is assumed to be empty and the output is - // discarded. - // - // Stdout and Stderr may refer to the same writer in which case the output is interspersed. - Stdin io.Reader Stdout io.Writer + + // Stderr is a pointer to a writer which receives the standard error stream. Stderr io.Writer } diff --git a/sample_configs/apparmor.json b/sample_configs/apparmor.json index d044524f..bc423f50 100644 --- a/sample_configs/apparmor.json +++ b/sample_configs/apparmor.json @@ -118,7 +118,7 @@ }, "restrict_sys": true, "apparmor_profile": "docker-default", - "device_nodes": [ + "devices": [ { "permissions": "rwm", "file_mode": 438, diff --git a/sample_configs/attach_to_bridge.json b/sample_configs/attach_to_bridge.json index 248a8a79..eb788691 100644 --- a/sample_configs/attach_to_bridge.json +++ b/sample_configs/attach_to_bridge.json @@ -117,7 +117,7 @@ "parent": "docker" }, "restrict_sys": true, - "device_nodes": [ + "devices": [ { "permissions": "rwm", "file_mode": 438, diff --git a/sample_configs/host-pid.json b/sample_configs/host-pid.json index 61c3cf48..5ef8f78c 100644 --- a/sample_configs/host-pid.json +++ b/sample_configs/host-pid.json @@ -117,7 +117,7 @@ "parent": "docker" }, "restrict_sys": true, - "device_nodes": [ + "devices": [ { "permissions": "rwm", "file_mode": 438, diff --git a/sample_configs/minimal.json b/sample_configs/minimal.json index 28b22cce..d6ee044c 100644 --- a/sample_configs/minimal.json +++ b/sample_configs/minimal.json @@ -117,7 +117,7 @@ "parent": "docker" }, "restrict_sys": true, - "device_nodes": [ + "devices": [ { "permissions": "rwm", "file_mode": 438, diff --git a/sample_configs/route_source_address_selection.json b/sample_configs/route_source_address_selection.json index b9c9ef7b..2ade6d00 100644 --- a/sample_configs/route_source_address_selection.json +++ b/sample_configs/route_source_address_selection.json @@ -117,7 +117,7 @@ "parent": "docker" }, "restrict_sys": true, - "device_nodes": [ + "devices": [ { "permissions": "rwm", "file_mode": 438, diff --git a/sample_configs/selinux.json b/sample_configs/selinux.json index a415c257..4d6971a4 100644 --- a/sample_configs/selinux.json +++ b/sample_configs/selinux.json @@ -119,7 +119,7 @@ "restrict_sys": true, "process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", "mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", - "device_nodes": [ + "devices": [ { "permissions": "rwm", "file_mode": 438, diff --git a/sample_configs/userns.json b/sample_configs/userns.json index 1ebbad61..ad10b249 100644 --- a/sample_configs/userns.json +++ b/sample_configs/userns.json @@ -117,7 +117,7 @@ "parent": "docker" }, "restrict_sys": true, - "device_nodes": [ + "devices": [ { "permissions": "rwm", "file_mode": 438,