From 20422c9bd9cbb936bcdd8cd37217d6b5e50f8bc4 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 10 Mar 2016 14:35:16 -0800 Subject: [PATCH] Update libcontainer to support rlimit per process This updates runc and libcontainer to handle rlimits per process and set them correctly for the container. Signed-off-by: Michael Crosby --- libcontainer/configs/config.go | 2 +- libcontainer/container_linux.go | 4 ++++ libcontainer/init_linux.go | 31 +++++++++++++++-------------- libcontainer/process.go | 6 ++++++ libcontainer/setns_init_linux.go | 2 +- libcontainer/standard_init_linux.go | 2 +- spec.go | 7 ------- utils.go | 17 +++++++++++++--- 8 files changed, 43 insertions(+), 28 deletions(-) diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index b8965f548..867c32ad6 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -138,7 +138,7 @@ type Config struct { // Rlimits specifies the resource limits, such as max open files, to set in the container // If Rlimits are not set, the container will inherit rlimits from the parent process - Rlimits []Rlimit `json:"rlimits"` + Rlimits []Rlimit `json:"rlimits,omitempty"` // OomScoreAdj specifies the adjustment to be made by the kernel when calculating oom scores // for a process. Valid values are between the range [-1000, '1000'], where processes with diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 8e308fa62..281278f71 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -332,6 +332,7 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig { NoNewPrivileges: c.config.NoNewPrivileges, AppArmorProfile: c.config.AppArmorProfile, ProcessLabel: c.config.ProcessLabel, + Rlimits: c.config.Rlimits, } if process.NoNewPrivileges != nil { cfg.NoNewPrivileges = *process.NoNewPrivileges @@ -342,6 +343,9 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig { if process.Label != "" { cfg.ProcessLabel = process.Label } + if len(process.Rlimits) > 0 { + cfg.Rlimits = process.Rlimits + } return cfg } diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index 8a61516d8..24e8f7146 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -44,19 +44,20 @@ type network struct { // initConfig is used for transferring parameters from Exec() to Init() type initConfig struct { - Args []string `json:"args"` - Env []string `json:"env"` - Cwd string `json:"cwd"` - Capabilities []string `json:"capabilities"` - ProcessLabel string `json:"process_label"` - AppArmorProfile string `json:"apparmor_profile"` - NoNewPrivileges bool `json:"no_new_privileges"` - User string `json:"user"` - Config *configs.Config `json:"config"` - Console string `json:"console"` - Networks []*network `json:"network"` - PassedFilesCount int `json:"passed_files_count"` - ContainerId string `json:"containerid"` + Args []string `json:"args"` + Env []string `json:"env"` + Cwd string `json:"cwd"` + Capabilities []string `json:"capabilities"` + ProcessLabel string `json:"process_label"` + AppArmorProfile string `json:"apparmor_profile"` + NoNewPrivileges bool `json:"no_new_privileges"` + User string `json:"user"` + Config *configs.Config `json:"config"` + Console string `json:"console"` + Networks []*network `json:"network"` + PassedFilesCount int `json:"passed_files_count"` + ContainerId string `json:"containerid"` + Rlimits []configs.Rlimit `json:"rlimits"` } type initer interface { @@ -315,8 +316,8 @@ func setupRoute(config *configs.Config) error { return nil } -func setupRlimits(config *configs.Config) error { - for _, rlimit := range config.Rlimits { +func setupRlimits(limits []configs.Rlimit) error { + for _, rlimit := range limits { l := &syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft} if err := syscall.Setrlimit(rlimit.Type, l); err != nil { return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err) diff --git a/libcontainer/process.go b/libcontainer/process.go index 9b82cfaca..91e8ef56f 100644 --- a/libcontainer/process.go +++ b/libcontainer/process.go @@ -5,6 +5,8 @@ import ( "io" "math" "os" + + "github.com/opencontainers/runc/libcontainer/configs" ) type processOperations interface { @@ -58,6 +60,10 @@ type Process struct { // NoNewPrivileges controls whether processes can gain additional privileges. NoNewPrivileges *bool + // Rlimits specifies the resource limits, such as max open files, to set in the container + // If Rlimits are not set, the container will inherit rlimits from the parent process + Rlimits []configs.Rlimit + ops processOperations } diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go index 5d78cc5be..33ef68e5a 100644 --- a/libcontainer/setns_init_linux.go +++ b/libcontainer/setns_init_linux.go @@ -28,7 +28,7 @@ func (l *linuxSetnsInit) Init() error { if _, err := keyctl.JoinSessionKeyring(l.getSessionRingName()); err != nil { return err } - if err := setupRlimits(l.config.Config); err != nil { + if err := setupRlimits(l.config.Rlimits); err != nil { return err } if l.config.NoNewPrivileges { diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go index 23604196f..2e1015053 100644 --- a/libcontainer/standard_init_linux.go +++ b/libcontainer/standard_init_linux.go @@ -73,7 +73,7 @@ func (l *linuxStandardInit) Init() error { if err := setupRoute(l.config.Config); err != nil { return err } - if err := setupRlimits(l.config.Config); err != nil { + if err := setupRlimits(l.config.Rlimits); err != nil { return err } diff --git a/spec.go b/spec.go index d1f0f2ca1..d5d66fcc9 100644 --- a/spec.go +++ b/spec.go @@ -278,13 +278,6 @@ func createLibcontainerConfig(cgroupName string, spec *specs.Spec) (*configs.Con if err := setupUserNamespace(spec, config); err != nil { return nil, err } - for _, rlimit := range spec.Process.Rlimits { - rl, err := createLibContainerRlimit(rlimit) - if err != nil { - return nil, err - } - config.Rlimits = append(config.Rlimits, rl) - } c, err := createCgroupConfig(cgroupName, spec) if err != nil { return nil, err diff --git a/utils.go b/utils.go index 6468c6e6b..59982fa73 100644 --- a/utils.go +++ b/utils.go @@ -232,8 +232,8 @@ func getDefaultImagePath(context *cli.Context) string { // newProcess returns a new libcontainer Process with the arguments from the // spec and stdio from the current process. -func newProcess(p specs.Process) *libcontainer.Process { - return &libcontainer.Process{ +func newProcess(p specs.Process) (*libcontainer.Process, error) { + lp := &libcontainer.Process{ Args: p.Args, Env: p.Env, // TODO: fix libcontainer's API to better support uid/gid in a typesafe way. @@ -244,6 +244,14 @@ func newProcess(p specs.Process) *libcontainer.Process { NoNewPrivileges: &p.NoNewPrivileges, AppArmorProfile: p.ApparmorProfile, } + for _, rlimit := range p.Rlimits { + rl, err := createLibContainerRlimit(rlimit) + if err != nil { + return nil, err + } + lp.Rlimits = append(lp.Rlimits, rl) + } + return lp, nil } func dupStdio(process *libcontainer.Process, rootuid int) error { @@ -332,7 +340,10 @@ func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcont // runProcess will create a new process in the specified container // by executing the process specified in the 'config'. func runProcess(container libcontainer.Container, config *specs.Process, listenFDs []*os.File, console string, pidFile string, detach bool) (int, error) { - process := newProcess(*config) + process, err := newProcess(*config) + if err != nil { + return -1, err + } // Add extra file descriptors if needed if len(listenFDs) > 0 {