diff --git a/configs/cgroup.go b/configs/cgroup.go index 0dffc640..92b9286a 100644 --- a/configs/cgroup.go +++ b/configs/cgroup.go @@ -9,46 +9,46 @@ const ( ) type Cgroup struct { - Name string `json:"name,omitempty"` + Name string `json:"name"` // name of parent cgroup or slice - Parent string `json:"parent,omitempty"` + Parent string `json:"parent"` // If this is true allow access to any kind of device within the container. If false, allow access only to devices explicitly listed in the allowed_devices list. - AllowAllDevices bool `json:"allow_all_devices,omitempty"` + AllowAllDevices bool `json:"allow_all_devices"` - AllowedDevices []*Device `json:"allowed_devices,omitempty"` + AllowedDevices []*Device `json:"allowed_devices"` // Memory limit (in bytes) - Memory int64 `json:"memory,omitempty"` + Memory int64 `json:"memory"` // Memory reservation or soft_limit (in bytes) - MemoryReservation int64 `json:"memory_reservation,omitempty"` + MemoryReservation int64 `json:"memory_reservation"` // Total memory usage (memory + swap); set `-1' to disable swap - MemorySwap int64 `json:"memory_swap,omitempty"` + MemorySwap int64 `json:"memory_swap"` // CPU shares (relative weight vs. other containers) - CpuShares int64 `json:"cpu_shares,omitempty"` + CpuShares int64 `json:"cpu_shares"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. - CpuQuota int64 `json:"cpu_quota,omitempty"` + CpuQuota int64 `json:"cpu_quota"` // CPU period to be used for hardcapping (in usecs). 0 to use system default. - CpuPeriod int64 `json:"cpu_period,omitempty"` + CpuPeriod int64 `json:"cpu_period"` // CPU to use - CpusetCpus string `json:"cpuset_cpus,omitempty"` + CpusetCpus string `json:"cpuset_cpus"` // MEM to use - CpusetMems string `json:"cpuset_mems,omitempty"` + CpusetMems string `json:"cpuset_mems"` // Specifies per cgroup weight, range is from 10 to 1000. - BlkioWeight int64 `json:"blkio_weight,omitempty"` + BlkioWeight int64 `json:"blkio_weight"` // set the freeze value for the process - Freezer FreezerState `json:"freezer,omitempty"` + Freezer FreezerState `json:"freezer"` // Parent slice to use for systemd TODO: remove in favor or parent - Slice string `json:"slice,omitempty"` + Slice string `json:"slice"` } diff --git a/configs/config.go b/configs/config.go index d8e2c9eb..fc7450d9 100644 --- a/configs/config.go +++ b/configs/config.go @@ -3,98 +3,98 @@ package configs import "fmt" type Rlimit struct { - Type int `json:"type,omitempty"` - Hard uint64 `json:"hard,omitempty"` - Soft uint64 `json:"soft,omitempty"` + Type int `json:"type"` + Hard uint64 `json:"hard"` + Soft uint64 `json:"soft"` } // IDMap represents UID/GID Mappings for User Namespaces. type IDMap struct { - ContainerID int `json:"container_id,omitempty"` - HostID int `json:"host_id,omitempty"` - Size int `json:"size,omitempty"` + ContainerID int `json:"container_id"` + HostID int `json:"host_id"` + Size int `json:"size"` } // Config defines configuration options for executing a process inside a contained environment. type Config struct { // NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs // This is a common option when the container is running in ramdisk - NoPivotRoot bool `json:"no_pivot_root,omitempty"` + NoPivotRoot bool `json:"no_pivot_root"` // ParentDeathSignal specifies the signal that is sent to the container's process in the case // that the parent process dies. - ParentDeathSignal int `json:"parent_death_signal,omitempty"` + ParentDeathSignal int `json:"parent_death_signal"` // PivotDir allows a custom directory inside the container's root filesystem to be used as pivot, when NoPivotRoot is not set. // When a custom PivotDir not set, a temporary dir inside the root filesystem will be used. The pivot dir needs to be writeable. // 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"` + PivotDir string `json:"pivot_dir"` // Path to a directory containing the container's root filesystem. - Rootfs string `json:"rootfs,omitempty"` + Rootfs string `json:"rootfs"` // Readonlyfs will remount the container's rootfs as readonly where only externally mounted // bind mounts are writtable. - Readonlyfs bool `json:"readonlyfs,omitempty"` + Readonlyfs bool `json:"readonlyfs"` // 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"` + Mounts []*Mount `json:"mounts"` // 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! - Devices []*Device `json:"devices,omitempty"` + Devices []*Device `json:"devices"` - MountLabel string `json:"mount_label,omitempty"` + MountLabel string `json:"mount_label"` // Hostname optionally sets the container's hostname if provided - Hostname string `json:"hostname,omitempty"` + Hostname string `json:"hostname"` // Console is the path to the console allocated to the container. - Console string `json:"console,omitempty"` + Console string `json:"console"` // Namespaces specifies the container's namespaces that it should setup when cloning the init process // If a namespace is not provided that namespace is shared from the container's parent process - Namespaces Namespaces `json:"namespaces,omitempty"` + Namespaces Namespaces `json:"namespaces"` // Capabilities specify the capabilities to keep when executing the process inside the container // All capbilities not specified will be dropped from the processes capability mask - Capabilities []string `json:"capabilities,omitempty"` + Capabilities []string `json:"capabilities"` // Networks specifies the container's network setup to be created - Networks []*Network `json:"networks,omitempty"` + Networks []*Network `json:"networks"` // Routes can be specified to create entries in the route table as the container is started - Routes []*Route `json:"routes,omitempty"` + Routes []*Route `json:"routes"` // Cgroups specifies specific cgroup settings for the various subsystems that the container is // placed into to limit the resources the container has available - Cgroups *Cgroup `json:"cgroups,omitempty"` + Cgroups *Cgroup `json:"cgroups"` // AppArmorProfile specifies the profile to apply to the process running in the container and is // change at the time the process is execed - AppArmorProfile string `json:"apparmor_profile,omitempty"` + AppArmorProfile string `json:"apparmor_profile"` // ProcessLabel specifies the label to apply to the process running in the container. It is // commonly used by selinux - ProcessLabel string `json:"process_label,omitempty"` + ProcessLabel string `json:"process_label"` // RestrictSys will remount /proc/sys, /sys, and mask over sysrq-trigger as well as /proc/irq and // /proc/bus - RestrictSys bool `json:"restrict_sys,omitempty"` + RestrictSys bool `json:"restrict_sys"` // 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,omitempty"` + Rlimits []Rlimit `json:"rlimits"` // AdditionalGroups specifies the gids that should be added to supplementary groups // in addition to those that the user belongs to. - AdditionalGroups []int `json:"additional_groups,omitempty"` + AdditionalGroups []int `json:"additional_groups"` // UidMappings is an array of User ID mappings for User Namespaces - UidMappings []IDMap `json:"uid_mappings,omitempty"` + UidMappings []IDMap `json:"uid_mappings"` // GidMappings is an array of Group ID mappings for User Namespaces - GidMappings []IDMap `json:"gid_mappings,omitempty"` + GidMappings []IDMap `json:"gid_mappings"` } // Gets the root uid for the process on host which could be non-zero diff --git a/configs/device.go b/configs/device.go index a8117068..abff2669 100644 --- a/configs/device.go +++ b/configs/device.go @@ -11,28 +11,28 @@ const ( type Device struct { // Device type, block, char, etc. - Type rune `json:"type,omitempty"` + Type rune `json:"type"` // Path to the device. - Path string `json:"path,omitempty"` + Path string `json:"path"` // Major is the device's major number. - Major int64 `json:"major,omitempty"` + Major int64 `json:"major"` // Minor is the device's minor number. - Minor int64 `json:"minor,omitempty"` + Minor int64 `json:"minor"` // Cgroup permissions format, rwm. - Permissions string `json:"permissions,omitempty"` + Permissions string `json:"permissions"` // FileMode permission bits for the device. - FileMode os.FileMode `json:"file_mode,omitempty"` + FileMode os.FileMode `json:"file_mode"` // Uid of the device. - Uid uint32 `json:"uid,omitempty"` + Uid uint32 `json:"uid"` // Gid of the device. - Gid uint32 `json:"gid,omitempty"` + Gid uint32 `json:"gid"` } func (d *Device) CgroupString() string { diff --git a/configs/mount.go b/configs/mount.go index 2f20de9e..eb26c5c0 100644 --- a/configs/mount.go +++ b/configs/mount.go @@ -1,11 +1,11 @@ package configs type Mount struct { - Type string `json:"type,omitempty"` - Source string `json:"source,omitempty"` // Source path, in the host namespace - Destination string `json:"destination,omitempty"` // Destination path, in the container - Writable bool `json:"writable,omitempty"` - Relabel string `json:"relabel,omitempty"` // Relabel source if set, "z" indicates shared, "Z" indicates unshared - Private bool `json:"private,omitempty"` - Slave bool `json:"slave,omitempty"` + Type string `json:"type"` + Source string `json:"source"` // Source path, in the host namespace + Destination string `json:"destination"` // Destination path, in the container + Writable bool `json:"writable"` + Relabel string `json:"relabel"` // Relabel source if set, "z" indicates shared, "Z" indicates unshared + Private bool `json:"private"` + Slave bool `json:"slave"` } diff --git a/configs/namespaces.go b/configs/namespaces.go index a227f1ba..fbb56e1d 100644 --- a/configs/namespaces.go +++ b/configs/namespaces.go @@ -19,7 +19,7 @@ const ( // alternate path that is able to be joined via setns. type Namespace struct { Type NamespaceType `json:"type"` - Path string `json:"path,omitempty"` + Path string `json:"path"` } func (n *Namespace) Syscall() int { diff --git a/configs/network.go b/configs/network.go index 890953a0..55443988 100644 --- a/configs/network.go +++ b/configs/network.go @@ -6,42 +6,42 @@ package configs // container to be setup with the host's networking stack type Network struct { // Type sets the networks type, commonly veth and loopback - Type string `json:"type,omitempty"` + Type string `json:"type"` // Name of the network interface - Name string `json:"name,omitempty"` + Name string `json:"name"` // The bridge to use. - Bridge string `json:"bridge,omitempty"` + Bridge string `json:"bridge"` // MacAddress contains the MAC address to set on the network interface - MacAddress string `json:"mac_address,omitempty"` + MacAddress string `json:"mac_address"` // Address contains the IPv4 and mask to set on the network interface - Address string `json:"address,omitempty"` + Address string `json:"address"` // Gateway sets the gateway address that is used as the default for the interface - Gateway string `json:"gateway,omitempty"` + Gateway string `json:"gateway"` // IPv6Address contains the IPv6 and mask to set on the network interface - IPv6Address string `json:"ipv6_address,omitempty"` + IPv6Address string `json:"ipv6_address"` // IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface - IPv6Gateway string `json:"ipv6_gateway,omitempty"` + IPv6Gateway string `json:"ipv6_gateway"` // Mtu sets the mtu value for the interface and will be mirrored on both the host and // container's interfaces if a pair is created, specifically in the case of type veth // Note: This does not apply to loopback interfaces. - Mtu int `json:"mtu,omitempty"` + Mtu int `json:"mtu"` // TxQueueLen sets the tx_queuelen value for the interface and will be mirrored on both the host and // container's interfaces if a pair is created, specifically in the case of type veth // Note: This does not apply to loopback interfaces. - TxQueueLen int `json:"txqueuelen,omitempty"` + TxQueueLen int `json:"txqueuelen"` // HostInterfaceName is a unique name of a veth pair that resides on in the host interface of the // container. - HostInterfaceName string `json:"host_interface_name,omitempty"` + HostInterfaceName string `json:"host_interface_name"` } // Routes can be specified to create entries in the route table as the container is started @@ -53,14 +53,14 @@ type Network struct { // destination of 0.0.0.0(or *) when viewed in the route table. type Route struct { // Sets the destination and mask, should be a CIDR. Accepts IPv4 and IPv6 - Destination string `json:"destination,omitempty"` + Destination string `json:"destination"` // Sets the source and mask, should be a CIDR. Accepts IPv4 and IPv6 - Source string `json:"source,omitempty"` + Source string `json:"source"` // Sets the gateway. Accepts IPv4 and IPv6 - Gateway string `json:"gateway,omitempty"` + Gateway string `json:"gateway"` // The device to set this route up for, for example: eth0 - InterfaceName string `json:"interface_name,omitempty"` + InterfaceName string `json:"interface_name"` } diff --git a/configs/state.go b/configs/state.go deleted file mode 100644 index 5e7eef9a..00000000 --- a/configs/state.go +++ /dev/null @@ -1,18 +0,0 @@ -package configs - -// The status of a container. -type Status int - -const ( - // The container exists and is running. - Running Status = iota + 1 - - // The container exists, it is in the process of being paused. - Pausing - - // The container exists, but all its processes are paused. - Paused - - // The container does not exist. - Destroyed -) diff --git a/container.go b/container.go index 6d681ac1..1954a994 100644 --- a/container.go +++ b/container.go @@ -9,21 +9,44 @@ import ( "github.com/docker/libcontainer/configs" ) +// The status of a container. +type Status int + +const ( + // The container exists and is running. + Running Status = iota + 1 + + // The container exists, it is in the process of being paused. + Pausing + + // The container exists, but all its processes are paused. + Paused + + // The container does not exist. + Destroyed +) + // State represents a running container's state type State struct { + // ID is the container ID. + ID string `json:"id"` + // InitProcessPid is the init process id in the parent namespace. - InitProcessPid int + InitProcessPid int `json:"init_process_pid"` // InitProcessStartTime is the init process start time. - InitProcessStartTime string + InitProcessStartTime string `json:"init_process_start"` // Path to all the cgroups setup for a container. Key is cgroup subsystem name // with the value as the path. - CgroupPaths map[string]string + CgroupPaths map[string]string `json:"cgroup_paths"` // NamespacePaths are filepaths to the container's namespaces. Key is the namespace name // with the value as the path. - NamespacePaths map[string]string + NamespacePaths map[string]string `json:"namespace_paths"` + + // Config is the container's configuration. + Config configs.Config `json:"config"` } // A libcontainer container object. @@ -40,7 +63,7 @@ type Container interface { // errors: // ContainerDestroyed - Container no longer exists, // Systemerror - System error. - Status() (configs.Status, error) + Status() (Status, error) // State returns the current container's state information. // diff --git a/generic_error.go b/generic_error.go index 08a47f61..5207822a 100644 --- a/generic_error.go +++ b/generic_error.go @@ -20,6 +20,9 @@ File: {{$frame.File}}{{end}} `)) func newGenericError(err error, c ErrorCode) Error { + if le, ok := err.(Error); ok { + return le + } return &GenericError{ Timestamp: time.Now(), Err: err, @@ -28,6 +31,18 @@ func newGenericError(err error, c ErrorCode) Error { } } +func newSystemError(err error) Error { + if le, ok := err.(Error); ok { + return le + } + return &GenericError{ + Timestamp: time.Now(), + Err: err, + ECode: SystemError, + Stack: stacktrace.Capture(2), + } +} + type GenericError struct { Timestamp time.Time ECode ErrorCode diff --git a/integration/exec_test.go b/integration/exec_test.go index 5e9fa0e4..4fef06ea 100644 --- a/integration/exec_test.go +++ b/integration/exec_test.go @@ -383,7 +383,7 @@ func TestFreeze(t *testing.T) { if err := container.Resume(); err != nil { t.Fatal(err) } - if state != configs.Paused { + if state != libcontainer.Paused { t.Fatal("Unexpected state: ", state) } diff --git a/linux_container.go b/linux_container.go index 50df5ba7..668cf06c 100644 --- a/linux_container.go +++ b/linux_container.go @@ -3,9 +3,11 @@ package libcontainer import ( + "encoding/json" "fmt" "os" "os/exec" + "path/filepath" "syscall" "github.com/docker/libcontainer/cgroups" @@ -32,21 +34,21 @@ func (c *linuxContainer) Config() configs.Config { return *c.config } -func (c *linuxContainer) Status() (configs.Status, error) { +func (c *linuxContainer) Status() (Status, error) { if c.initProcess == nil { - return configs.Destroyed, nil + return Destroyed, nil } // return Running if the init process is alive if err := syscall.Kill(c.initProcess.pid(), 0); err != nil { if err == syscall.ESRCH { - return configs.Destroyed, nil + return Destroyed, nil } - return 0, err + return 0, newSystemError(err) } if c.config.Cgroups != nil && c.config.Cgroups.Freezer == configs.Frozen { - return configs.Paused, nil + return Paused, nil } - return configs.Running, nil + return Running, nil } func (c *linuxContainer) State() (*State, error) { @@ -54,14 +56,16 @@ func (c *linuxContainer) State() (*State, error) { if err != nil { return nil, err } - if status == configs.Destroyed { + if status == Destroyed { return nil, newGenericError(fmt.Errorf("container destroyed"), ContainerNotExists) } startTime, err := c.initProcess.startTime() if err != nil { - return nil, err + return nil, newSystemError(err) } state := &State{ + ID: c.ID(), + Config: *c.config, InitProcessPid: c.initProcess.pid(), InitProcessStartTime: startTime, CgroupPaths: c.cgroupManager.GetPaths(), @@ -96,7 +100,7 @@ func (c *linuxContainer) Processes() ([]int, error) { glog.Info("fetch container processes") pids, err := c.cgroupManager.GetPids() if err != nil { - return nil, newGenericError(err, SystemError) + return nil, newSystemError(err) } return pids, nil } @@ -108,14 +112,14 @@ func (c *linuxContainer) Stats() (*Stats, error) { stats = &Stats{} ) if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil { - return stats, newGenericError(err, SystemError) + return stats, newSystemError(err) } for _, iface := range c.config.Networks { switch iface.Type { case "veth": istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) if err != nil { - return stats, newGenericError(err, SystemError) + return stats, newSystemError(err) } stats.Interfaces = append(stats.Interfaces, istats) } @@ -128,20 +132,20 @@ func (c *linuxContainer) Start(process *Process) (int, error) { if err != nil { return -1, err } - doInit := status == configs.Destroyed + doInit := status == Destroyed parent, err := c.newParentProcess(process, doInit) if err != nil { - return -1, err + return -1, newSystemError(err) } if err := parent.start(); err != nil { // terminate the process to ensure that it properly is reaped. if err := parent.terminate(); err != nil { glog.Warning(err) } - return -1, err + return -1, newSystemError(err) } if doInit { - c.initProcess = parent + c.updateState(parent) } return parent.pid(), nil } @@ -149,11 +153,11 @@ func (c *linuxContainer) Start(process *Process) (int, error) { func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) { parentPipe, childPipe, err := newPipe() if err != nil { - return nil, err + return nil, newSystemError(err) } cmd, err := c.commandTemplate(p, childPipe) if err != nil { - return nil, err + return nil, newSystemError(err) } if !doInit { return c.newSetnsProcess(p, cmd, parentPipe, childPipe), nil @@ -171,7 +175,10 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec. cmd.SysProcAttr = &syscall.SysProcAttr{} } cmd.ExtraFiles = []*os.File{childPipe} - cmd.SysProcAttr.Pdeathsig = syscall.Signal(c.config.ParentDeathSignal) + cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL + if c.config.ParentDeathSignal > 0 { + cmd.SysProcAttr.Pdeathsig = syscall.Signal(c.config.ParentDeathSignal) + } return cmd, nil } @@ -254,11 +261,19 @@ func (c *linuxContainer) Destroy() error { if err != nil { return err } - if status != configs.Destroyed { + if status != Destroyed { return newGenericError(nil, ContainerNotStopped) } - // TODO: remove cgroups - return os.RemoveAll(c.root) + if !c.config.Namespaces.Contains(configs.NEWPID) { + if err := killCgroupProcesses(c.cgroupManager); err != nil { + glog.Warning(err) + } + } + err = c.cgroupManager.Destroy() + if rerr := os.RemoveAll(c.root); err == nil { + err = rerr + } + return err } func (c *linuxContainer) Pause() error { @@ -270,11 +285,23 @@ func (c *linuxContainer) Resume() error { } func (c *linuxContainer) Signal(signal os.Signal) error { - glog.Infof("sending signal %d to pid %d", signal, c.initProcess.pid()) return c.initProcess.signal(signal) } -// TODO: rename to be more descriptive func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) { return NotifyOnOOM(c.cgroupManager.GetPaths()) } + +func (c *linuxContainer) updateState(process parentProcess) error { + c.initProcess = process + state, err := c.State() + if err != nil { + return err + } + f, err := os.Create(filepath.Join(c.root, stateFilename)) + if err != nil { + return err + } + defer f.Close() + return json.NewEncoder(f).Encode(state) +} diff --git a/linux_factory.go b/linux_factory.go index 8c33e098..66823953 100644 --- a/linux_factory.go +++ b/linux_factory.go @@ -18,8 +18,7 @@ import ( ) const ( - configFilename = "config.json" - stateFilename = "state.json" + stateFilename = "state.json" ) var ( @@ -65,23 +64,9 @@ func (l *linuxFactory) Create(id string, config *configs.Config) (Container, err } else if !os.IsNotExist(err) { return nil, newGenericError(err, SystemError) } - data, err := json.MarshalIndent(config, "", "\t") - if err != nil { - return nil, newGenericError(err, SystemError) - } if err := os.MkdirAll(containerRoot, 0700); err != nil { return nil, newGenericError(err, SystemError) } - f, err := os.Create(filepath.Join(containerRoot, configFilename)) - if err != nil { - os.RemoveAll(containerRoot) - return nil, newGenericError(err, SystemError) - } - defer f.Close() - if _, err := f.Write(data); err != nil { - os.RemoveAll(containerRoot) - return nil, newGenericError(err, SystemError) - } return &linuxContainer{ id: id, root: containerRoot, @@ -96,13 +81,7 @@ func (l *linuxFactory) Load(id string) (Container, error) { return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid) } containerRoot := filepath.Join(l.root, id) - glog.Infof("loading container config from %s", containerRoot) - config, err := l.loadContainerConfig(containerRoot) - if err != nil { - return nil, err - } - glog.Infof("loading container state from %s", containerRoot) - state, err := l.loadContainerState(containerRoot) + state, err := l.loadState(containerRoot) if err != nil { return nil, err } @@ -110,12 +89,12 @@ func (l *linuxFactory) Load(id string) (Container, error) { processPid: state.InitProcessPid, processStartTime: state.InitProcessStartTime, } - cgroupManager := cgroups.LoadCgroupManager(config.Cgroups, state.CgroupPaths) + cgroupManager := cgroups.LoadCgroupManager(state.Config.Cgroups, state.CgroupPaths) glog.Infof("using %s as cgroup manager", cgroupManager) return &linuxContainer{ initProcess: r, id: id, - config: config, + config: &state.Config, initArgs: l.initArgs, cgroupManager: cgroupManager, root: containerRoot, @@ -155,23 +134,7 @@ func (l *linuxFactory) StartInitialization(pipefd uintptr) (err error) { return i.Init() } -func (l *linuxFactory) loadContainerConfig(root string) (*configs.Config, error) { - f, err := os.Open(filepath.Join(root, configFilename)) - if err != nil { - if os.IsNotExist(err) { - return nil, newGenericError(err, ContainerNotExists) - } - return nil, newGenericError(err, SystemError) - } - defer f.Close() - var config *configs.Config - if err := json.NewDecoder(f).Decode(&config); err != nil { - return nil, newGenericError(err, ConfigInvalid) - } - return config, nil -} - -func (l *linuxFactory) loadContainerState(root string) (*State, error) { +func (l *linuxFactory) loadState(root string) (*State, error) { f, err := os.Open(filepath.Join(root, stateFilename)) if err != nil { if os.IsNotExist(err) { diff --git a/linux_factory_test.go b/linux_factory_test.go index 457ec10d..69001669 100644 --- a/linux_factory_test.go +++ b/linux_factory_test.go @@ -82,14 +82,12 @@ func TestFactoryLoadContainer(t *testing.T) { } expectedState = &State{ InitProcessPid: 1024, + Config: *expectedConfig, } ) if err := os.Mkdir(filepath.Join(root, id), 0700); err != nil { t.Fatal(err) } - if err := marshal(filepath.Join(root, id, configFilename), expectedConfig); err != nil { - t.Fatal(err) - } if err := marshal(filepath.Join(root, id, stateFilename), expectedState); err != nil { t.Fatal(err) } diff --git a/linux_init.go b/linux_init.go index 7c56afe8..9ed27e89 100644 --- a/linux_init.go +++ b/linux_init.go @@ -9,11 +9,13 @@ import ( "strings" "syscall" + "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/netlink" "github.com/docker/libcontainer/system" "github.com/docker/libcontainer/user" "github.com/docker/libcontainer/utils" + "github.com/golang/glog" ) type initType string @@ -226,3 +228,35 @@ func setupRlimits(config *configs.Config) error { } return nil } + +// killCgroupProcesses freezes then itterates over all the processes inside the +// manager's cgroups sending a SIGKILL to each process then waiting for them to +// exit. +func killCgroupProcesses(m cgroups.Manager) error { + var procs []*os.Process + if err := m.Freeze(configs.Frozen); err != nil { + glog.Warning(err) + } + pids, err := m.GetPids() + if err != nil { + m.Freeze(configs.Thawed) + return err + } + for _, pid := range pids { + if p, err := os.FindProcess(pid); err == nil { + procs = append(procs, p) + if err := p.Kill(); err != nil { + glog.Warning(err) + } + } + } + if err := m.Freeze(configs.Thawed); err != nil { + glog.Warning(err) + } + for _, p := range procs { + if _, err := p.Wait(); err != nil { + glog.Warning(err) + } + } + return nil +} diff --git a/linux_process.go b/linux_process.go index 2b434a51..955df882 100644 --- a/linux_process.go +++ b/linux_process.go @@ -11,7 +11,6 @@ import ( "syscall" "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/system" "github.com/golang/glog" ) @@ -184,27 +183,7 @@ func (p *initProcess) wait() (*os.ProcessState, error) { } // we should kill all processes in cgroup when init is died if we use host PID namespace if p.cmd.SysProcAttr.Cloneflags&syscall.CLONE_NEWPID == 0 { - // TODO: this will not work for the success path because libcontainer - // does not wait on the process. This needs to be moved to destroy or add a Wait() - // method back onto the container. - var procs []*os.Process - p.manager.Freeze(configs.Frozen) - pids, err := p.manager.GetPids() - if err != nil { - return nil, err - } - for _, pid := range pids { - // TODO: log err without aborting if we are unable to find - // a single PID - if p, err := os.FindProcess(pid); err == nil { - procs = append(procs, p) - p.Kill() - } - } - p.manager.Freeze(configs.Thawed) - for _, p := range procs { - p.Wait() - } + killCgroupProcesses(p.manager) } return state, nil } diff --git a/nsinit/init.go b/nsinit/init.go index 9848c42b..ea6295e5 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -12,9 +12,6 @@ import ( var initCommand = cli.Command{ Name: "init", Usage: "runs the init process inside the namespace", - Flags: []cli.Flag{ - cli.IntFlag{Name: "fd", Value: 0, Usage: "internal pipe fd"}, - }, Action: func(context *cli.Context) { runtime.GOMAXPROCS(1) runtime.LockOSThread() @@ -22,11 +19,7 @@ var initCommand = cli.Command{ if err != nil { log.Fatal(err) } - if context.Int("fd") == 0 { - log.Fatal("--fd must be specified for init process") - } - fd := uintptr(context.Int("fd")) - if err := factory.StartInitialization(fd); err != nil { + if err := factory.StartInitialization(3); err != nil { log.Fatal(err) } panic("This line should never been executed") diff --git a/nsinit/utils.go b/nsinit/utils.go index 901972e8..62d8e6fd 100644 --- a/nsinit/utils.go +++ b/nsinit/utils.go @@ -24,7 +24,7 @@ func loadConfig(context *cli.Context) (*configs.Config, error) { } func loadFactory(context *cli.Context) (libcontainer.Factory, error) { - return libcontainer.New(context.GlobalString("root"), []string{os.Args[0], "init", "--fd", "3", "--"}) + return libcontainer.New(context.GlobalString("root"), []string{os.Args[0], "init"}) } func getContainer(context *cli.Context) (libcontainer.Container, error) {