2014-10-23 04:45:23 +08:00
|
|
|
// +build linux
|
|
|
|
|
|
|
|
package libcontainer
|
|
|
|
|
2014-10-31 06:08:28 +08:00
|
|
|
import (
|
2015-02-12 08:45:23 +08:00
|
|
|
"encoding/json"
|
2014-12-15 23:05:11 +08:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2015-02-12 08:45:23 +08:00
|
|
|
"path/filepath"
|
2014-12-15 23:05:11 +08:00
|
|
|
"syscall"
|
|
|
|
|
2015-01-13 05:54:00 +08:00
|
|
|
"github.com/docker/libcontainer/cgroups"
|
2014-12-17 17:12:23 +08:00
|
|
|
"github.com/docker/libcontainer/configs"
|
2014-12-06 09:02:49 +08:00
|
|
|
"github.com/golang/glog"
|
2014-10-31 06:08:28 +08:00
|
|
|
)
|
2014-10-23 04:45:23 +08:00
|
|
|
|
|
|
|
type linuxContainer struct {
|
2014-10-23 07:53:28 +08:00
|
|
|
id string
|
|
|
|
root string
|
2014-12-17 17:12:23 +08:00
|
|
|
config *configs.Config
|
2015-01-13 05:54:00 +08:00
|
|
|
cgroupManager cgroups.Manager
|
2014-12-15 23:05:11 +08:00
|
|
|
initArgs []string
|
2015-02-07 13:12:27 +08:00
|
|
|
initProcess parentProcess
|
2014-10-23 04:45:23 +08:00
|
|
|
}
|
|
|
|
|
2015-02-01 13:21:06 +08:00
|
|
|
// ID returns the container's unique ID
|
2014-10-23 04:45:23 +08:00
|
|
|
func (c *linuxContainer) ID() string {
|
|
|
|
return c.id
|
|
|
|
}
|
|
|
|
|
2015-02-01 13:21:06 +08:00
|
|
|
// Config returns the container's configuration
|
|
|
|
func (c *linuxContainer) Config() configs.Config {
|
|
|
|
return *c.config
|
2014-10-23 04:45:23 +08:00
|
|
|
}
|
|
|
|
|
2015-02-12 08:45:23 +08:00
|
|
|
func (c *linuxContainer) Status() (Status, error) {
|
2015-02-07 13:12:27 +08:00
|
|
|
if c.initProcess == nil {
|
2015-02-12 08:45:23 +08:00
|
|
|
return Destroyed, nil
|
2014-12-23 21:09:35 +08:00
|
|
|
}
|
|
|
|
// return Running if the init process is alive
|
2015-02-07 13:12:27 +08:00
|
|
|
if err := syscall.Kill(c.initProcess.pid(), 0); err != nil {
|
2014-12-23 06:06:22 +08:00
|
|
|
if err == syscall.ESRCH {
|
2015-02-12 08:45:23 +08:00
|
|
|
return Destroyed, nil
|
2014-12-23 21:09:35 +08:00
|
|
|
}
|
2015-02-12 08:45:23 +08:00
|
|
|
return 0, newSystemError(err)
|
2014-12-23 21:09:35 +08:00
|
|
|
}
|
2015-02-12 06:45:07 +08:00
|
|
|
if c.config.Cgroups != nil && c.config.Cgroups.Freezer == configs.Frozen {
|
2015-02-12 08:45:23 +08:00
|
|
|
return Paused, nil
|
2015-01-19 21:19:28 +08:00
|
|
|
}
|
2015-02-12 08:45:23 +08:00
|
|
|
return Running, nil
|
2014-10-23 04:45:23 +08:00
|
|
|
}
|
|
|
|
|
2015-02-12 06:45:07 +08:00
|
|
|
func (c *linuxContainer) State() (*State, error) {
|
|
|
|
status, err := c.Status()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-02-12 08:45:23 +08:00
|
|
|
if status == Destroyed {
|
2015-02-12 06:45:07 +08:00
|
|
|
return nil, newGenericError(fmt.Errorf("container destroyed"), ContainerNotExists)
|
|
|
|
}
|
|
|
|
startTime, err := c.initProcess.startTime()
|
|
|
|
if err != nil {
|
2015-02-12 08:45:23 +08:00
|
|
|
return nil, newSystemError(err)
|
2015-02-12 06:45:07 +08:00
|
|
|
}
|
|
|
|
state := &State{
|
2015-02-12 08:45:23 +08:00
|
|
|
ID: c.ID(),
|
|
|
|
Config: *c.config,
|
2015-02-12 06:45:07 +08:00
|
|
|
InitProcessPid: c.initProcess.pid(),
|
|
|
|
InitProcessStartTime: startTime,
|
|
|
|
CgroupPaths: c.cgroupManager.GetPaths(),
|
|
|
|
NamespacePaths: make(map[string]string),
|
|
|
|
}
|
|
|
|
for _, ns := range c.config.Namespaces {
|
|
|
|
if ns.Path != "" {
|
|
|
|
state.NamespacePaths[string(ns.Type)] = ns.Path
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
file := ""
|
|
|
|
switch ns.Type {
|
|
|
|
case configs.NEWNET:
|
|
|
|
file = "net"
|
|
|
|
case configs.NEWNS:
|
|
|
|
file = "mnt"
|
|
|
|
case configs.NEWPID:
|
|
|
|
file = "pid"
|
|
|
|
case configs.NEWIPC:
|
|
|
|
file = "ipc"
|
|
|
|
case configs.NEWUSER:
|
|
|
|
file = "user"
|
|
|
|
case configs.NEWUTS:
|
|
|
|
file = "uts"
|
|
|
|
}
|
|
|
|
state.NamespacePaths[string(ns.Type)] = fmt.Sprintf("/proc/%d/ns/%s", c.initProcess.pid(), file)
|
|
|
|
}
|
|
|
|
return state, nil
|
|
|
|
}
|
|
|
|
|
2014-10-23 07:27:06 +08:00
|
|
|
func (c *linuxContainer) Processes() ([]int, error) {
|
2014-12-06 09:06:58 +08:00
|
|
|
glog.Info("fetch container processes")
|
2014-12-06 09:02:49 +08:00
|
|
|
pids, err := c.cgroupManager.GetPids()
|
2014-10-23 04:45:23 +08:00
|
|
|
if err != nil {
|
2015-02-12 08:45:23 +08:00
|
|
|
return nil, newSystemError(err)
|
2014-10-23 04:45:23 +08:00
|
|
|
}
|
|
|
|
return pids, nil
|
|
|
|
}
|
|
|
|
|
2015-02-01 11:56:27 +08:00
|
|
|
func (c *linuxContainer) Stats() (*Stats, error) {
|
2014-12-06 09:06:58 +08:00
|
|
|
glog.Info("fetch container stats")
|
2014-10-23 04:45:23 +08:00
|
|
|
var (
|
|
|
|
err error
|
2015-02-01 11:56:27 +08:00
|
|
|
stats = &Stats{}
|
2014-10-23 04:45:23 +08:00
|
|
|
)
|
2014-12-06 09:02:49 +08:00
|
|
|
if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil {
|
2015-02-12 08:45:23 +08:00
|
|
|
return stats, newSystemError(err)
|
2014-10-23 04:45:23 +08:00
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
for _, iface := range c.config.Networks {
|
2015-02-10 07:16:27 +08:00
|
|
|
switch iface.Type {
|
|
|
|
case "veth":
|
2015-02-11 03:51:45 +08:00
|
|
|
istats, err := getNetworkInterfaceStats(iface.HostInterfaceName)
|
2015-02-10 07:16:27 +08:00
|
|
|
if err != nil {
|
2015-02-12 08:45:23 +08:00
|
|
|
return stats, newSystemError(err)
|
2015-02-07 13:12:27 +08:00
|
|
|
}
|
2015-02-10 07:16:27 +08:00
|
|
|
stats.Interfaces = append(stats.Interfaces, istats)
|
2015-02-07 13:12:27 +08:00
|
|
|
}
|
2014-10-23 04:45:23 +08:00
|
|
|
}
|
|
|
|
return stats, nil
|
|
|
|
}
|
2014-10-28 08:51:14 +08:00
|
|
|
|
2015-02-01 12:51:12 +08:00
|
|
|
func (c *linuxContainer) Start(process *Process) (int, error) {
|
2015-02-01 11:56:27 +08:00
|
|
|
status, err := c.Status()
|
2014-12-15 23:05:11 +08:00
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
2015-02-12 08:45:23 +08:00
|
|
|
doInit := status == Destroyed
|
2015-02-07 13:12:27 +08:00
|
|
|
parent, err := c.newParentProcess(process, doInit)
|
|
|
|
if err != nil {
|
2015-02-12 08:45:23 +08:00
|
|
|
return -1, newSystemError(err)
|
2015-02-07 04:48:57 +08:00
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
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)
|
|
|
|
}
|
2015-02-12 08:45:23 +08:00
|
|
|
return -1, newSystemError(err)
|
2015-02-07 04:48:57 +08:00
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
if doInit {
|
2015-02-12 08:45:23 +08:00
|
|
|
c.updateState(parent)
|
2015-02-07 13:12:27 +08:00
|
|
|
}
|
|
|
|
return parent.pid(), nil
|
2015-02-07 04:48:57 +08:00
|
|
|
}
|
|
|
|
|
2015-02-07 13:12:27 +08:00
|
|
|
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
|
|
|
|
parentPipe, childPipe, err := newPipe()
|
|
|
|
if err != nil {
|
2015-02-12 08:45:23 +08:00
|
|
|
return nil, newSystemError(err)
|
2015-02-07 13:12:27 +08:00
|
|
|
}
|
|
|
|
cmd, err := c.commandTemplate(p, childPipe)
|
|
|
|
if err != nil {
|
2015-02-12 08:45:23 +08:00
|
|
|
return nil, newSystemError(err)
|
2015-02-07 13:12:27 +08:00
|
|
|
}
|
|
|
|
if !doInit {
|
|
|
|
return c.newSetnsProcess(p, cmd, parentPipe, childPipe), nil
|
|
|
|
}
|
|
|
|
return c.newInitProcess(p, cmd, parentPipe, childPipe), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) {
|
2014-12-23 06:06:22 +08:00
|
|
|
cmd := exec.Command(c.initArgs[0], c.initArgs[1:]...)
|
2015-02-07 13:12:27 +08:00
|
|
|
cmd.Stdin = p.Stdin
|
|
|
|
cmd.Stdout = p.Stdout
|
|
|
|
cmd.Stderr = p.Stderr
|
2015-02-04 09:44:58 +08:00
|
|
|
cmd.Dir = c.config.Rootfs
|
2014-12-23 06:06:22 +08:00
|
|
|
if cmd.SysProcAttr == nil {
|
|
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
cmd.ExtraFiles = []*os.File{childPipe}
|
2015-02-12 08:45:23 +08:00
|
|
|
cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
|
|
|
|
if c.config.ParentDeathSignal > 0 {
|
|
|
|
cmd.SysProcAttr.Pdeathsig = syscall.Signal(c.config.ParentDeathSignal)
|
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
return cmd, nil
|
2014-12-15 23:05:11 +08:00
|
|
|
}
|
|
|
|
|
2015-02-07 13:12:27 +08:00
|
|
|
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) *initProcess {
|
2015-02-11 03:51:45 +08:00
|
|
|
t := "_LIBCONTAINER_INITTYPE=standard"
|
2015-02-07 13:12:27 +08:00
|
|
|
cloneFlags := c.config.Namespaces.CloneFlags()
|
|
|
|
if cloneFlags&syscall.CLONE_NEWUSER != 0 {
|
|
|
|
c.addUidGidMappings(cmd.SysProcAttr)
|
|
|
|
// Default to root user when user namespaces are enabled.
|
|
|
|
if cmd.SysProcAttr.Credential == nil {
|
|
|
|
cmd.SysProcAttr.Credential = &syscall.Credential{}
|
2015-02-07 04:48:57 +08:00
|
|
|
}
|
2015-02-11 03:51:45 +08:00
|
|
|
t = "_LIBCONTAINER_INITTYPE=userns"
|
2015-02-01 13:21:06 +08:00
|
|
|
}
|
2015-02-11 03:51:45 +08:00
|
|
|
cmd.Env = append(cmd.Env, t)
|
2015-02-07 13:12:27 +08:00
|
|
|
cmd.SysProcAttr.Cloneflags = cloneFlags
|
|
|
|
return &initProcess{
|
|
|
|
cmd: cmd,
|
|
|
|
childPipe: childPipe,
|
|
|
|
parentPipe: parentPipe,
|
|
|
|
manager: c.cgroupManager,
|
|
|
|
config: c.newInitConfig(p),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) *setnsProcess {
|
|
|
|
cmd.Env = append(cmd.Env,
|
|
|
|
fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()),
|
|
|
|
"_LIBCONTAINER_INITTYPE=setns",
|
|
|
|
)
|
|
|
|
// TODO: set on container for process management
|
|
|
|
return &setnsProcess{
|
|
|
|
cmd: cmd,
|
|
|
|
cgroupPaths: c.cgroupManager.GetPaths(),
|
|
|
|
childPipe: childPipe,
|
|
|
|
parentPipe: parentPipe,
|
|
|
|
config: c.newInitConfig(p),
|
2015-02-01 13:21:06 +08:00
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
|
|
|
|
return &initConfig{
|
2015-02-01 13:21:06 +08:00
|
|
|
Config: c.config,
|
2015-02-07 11:16:11 +08:00
|
|
|
Args: process.Args,
|
|
|
|
Env: process.Env,
|
2015-02-12 09:42:58 +08:00
|
|
|
User: process.User,
|
|
|
|
Cwd: process.Cwd,
|
2015-02-01 13:21:06 +08:00
|
|
|
}
|
2014-12-15 23:05:11 +08:00
|
|
|
}
|
|
|
|
|
2015-02-07 13:12:27 +08:00
|
|
|
// Converts IDMap to SysProcIDMap array and adds it to SysProcAttr.
|
|
|
|
func (c *linuxContainer) addUidGidMappings(sys *syscall.SysProcAttr) {
|
|
|
|
if c.config.UidMappings != nil {
|
|
|
|
sys.UidMappings = make([]syscall.SysProcIDMap, len(c.config.UidMappings))
|
|
|
|
for i, um := range c.config.UidMappings {
|
|
|
|
sys.UidMappings[i].ContainerID = um.ContainerID
|
|
|
|
sys.UidMappings[i].HostID = um.HostID
|
|
|
|
sys.UidMappings[i].Size = um.Size
|
2015-02-01 13:21:06 +08:00
|
|
|
}
|
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
if c.config.GidMappings != nil {
|
|
|
|
sys.GidMappings = make([]syscall.SysProcIDMap, len(c.config.GidMappings))
|
|
|
|
for i, gm := range c.config.GidMappings {
|
|
|
|
sys.GidMappings[i].ContainerID = gm.ContainerID
|
|
|
|
sys.GidMappings[i].HostID = gm.HostID
|
|
|
|
sys.GidMappings[i].Size = gm.Size
|
2015-02-01 13:21:06 +08:00
|
|
|
}
|
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func newPipe() (parent *os.File, child *os.File, err error) {
|
|
|
|
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
2015-02-01 13:21:06 +08:00
|
|
|
if err != nil {
|
2015-02-07 13:12:27 +08:00
|
|
|
return nil, nil, err
|
2015-02-01 13:21:06 +08:00
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil
|
2014-10-28 08:51:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *linuxContainer) Destroy() error {
|
2015-02-01 11:56:27 +08:00
|
|
|
status, err := c.Status()
|
2014-12-15 23:00:04 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-02-12 08:45:23 +08:00
|
|
|
if status != Destroyed {
|
2014-12-15 23:00:04 +08:00
|
|
|
return newGenericError(nil, ContainerNotStopped)
|
|
|
|
}
|
2015-02-12 08:45:23 +08:00
|
|
|
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
|
|
|
|
}
|
2015-02-12 09:42:58 +08:00
|
|
|
c.initProcess = nil
|
2015-02-12 08:45:23 +08:00
|
|
|
return err
|
2014-10-28 08:51:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *linuxContainer) Pause() error {
|
2015-02-01 11:56:27 +08:00
|
|
|
return c.cgroupManager.Freeze(configs.Frozen)
|
2014-10-28 08:51:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *linuxContainer) Resume() error {
|
2015-02-01 11:56:27 +08:00
|
|
|
return c.cgroupManager.Freeze(configs.Thawed)
|
2014-10-28 08:51:14 +08:00
|
|
|
}
|
|
|
|
|
2015-02-01 12:51:12 +08:00
|
|
|
func (c *linuxContainer) Signal(signal os.Signal) error {
|
2015-02-12 09:42:58 +08:00
|
|
|
if c.initProcess == nil {
|
|
|
|
return newGenericError(nil, ContainerNotRunning)
|
|
|
|
}
|
2015-02-07 14:33:10 +08:00
|
|
|
return c.initProcess.signal(signal)
|
2014-10-28 08:51:14 +08:00
|
|
|
}
|
|
|
|
|
2015-02-12 07:09:54 +08:00
|
|
|
func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
|
2015-02-12 09:12:03 +08:00
|
|
|
return notifyOnOOM(c.cgroupManager.GetPaths())
|
2015-02-01 13:21:06 +08:00
|
|
|
}
|
2015-02-12 08:45:23 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|