2014-02-24 20:41:09 -08:00
|
|
|
package native
|
2014-02-21 17:11:57 -08:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
2014-02-24 15:47:23 -08:00
|
|
|
"strconv"
|
2014-02-21 17:11:57 -08:00
|
|
|
"strings"
|
|
|
|
"syscall"
|
2014-04-21 10:31:51 -07:00
|
|
|
|
|
|
|
"github.com/dotcloud/docker/daemon/execdriver"
|
|
|
|
"github.com/dotcloud/docker/pkg/apparmor"
|
|
|
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
2014-05-14 15:21:44 -07:00
|
|
|
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
2014-04-21 10:31:51 -07:00
|
|
|
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
|
|
|
|
"github.com/dotcloud/docker/pkg/system"
|
2014-02-21 17:11:57 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2014-04-08 10:10:51 -07:00
|
|
|
DriverName = "native"
|
2014-04-11 15:31:45 +00:00
|
|
|
Version = "0.2"
|
2014-04-08 10:10:51 -07:00
|
|
|
BackupApparmorProfilePath = "apparmor/docker.back" // relative to docker root
|
2014-02-21 17:11:57 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
2014-04-30 18:20:01 -07:00
|
|
|
var container *libcontainer.Container
|
2014-02-25 12:41:31 -08:00
|
|
|
f, err := os.Open(filepath.Join(args.Root, "container.json"))
|
2014-02-24 12:21:13 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := json.NewDecoder(f).Decode(&container); err != nil {
|
|
|
|
f.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
|
2014-04-30 18:20:01 -07:00
|
|
|
rootfs, err := os.Getwd()
|
2014-02-24 12:21:13 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(args.Pipe))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-04-30 18:20:01 -07:00
|
|
|
if err := nsinit.Init(container, rootfs, args.Console, syncPipe, args.Args); err != nil {
|
2014-02-24 12:21:13 -08:00
|
|
|
return err
|
|
|
|
}
|
2014-02-21 17:11:57 -08:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type driver struct {
|
2014-03-21 00:48:17 +00:00
|
|
|
root string
|
|
|
|
initPath string
|
2014-03-21 08:10:07 +00:00
|
|
|
activeContainers map[string]*exec.Cmd
|
2014-02-21 17:11:57 -08:00
|
|
|
}
|
|
|
|
|
2014-03-03 16:15:29 +01:00
|
|
|
func NewDriver(root, initPath string) (*driver, error) {
|
2014-02-26 19:19:14 -08:00
|
|
|
if err := os.MkdirAll(root, 0700); err != nil {
|
2014-02-25 12:41:31 -08:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-04-08 10:10:51 -07:00
|
|
|
// native driver root is at docker_root/execdriver/native. Put apparmor at docker_root
|
|
|
|
if err := apparmor.InstallDefaultProfile(filepath.Join(root, "../..", BackupApparmorProfilePath)); err != nil {
|
2014-03-05 14:57:20 -08:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-02-24 14:11:09 -08:00
|
|
|
return &driver{
|
2014-03-21 00:48:17 +00:00
|
|
|
root: root,
|
|
|
|
initPath: initPath,
|
2014-03-21 08:10:07 +00:00
|
|
|
activeContainers: make(map[string]*exec.Cmd),
|
2014-02-24 14:11:09 -08:00
|
|
|
}, nil
|
2014-02-21 17:11:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
2014-03-21 08:10:07 +00:00
|
|
|
// take the Command and populate the libcontainer.Container from it
|
|
|
|
container, err := d.createContainer(c)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
d.activeContainers[c.ID] = &c.Cmd
|
2014-03-21 00:48:17 +00:00
|
|
|
|
2014-02-21 17:11:57 -08:00
|
|
|
var (
|
2014-04-30 18:20:01 -07:00
|
|
|
dataPath = filepath.Join(d.root, c.ID)
|
|
|
|
args = append([]string{c.Entrypoint}, c.Arguments...)
|
2014-02-22 01:21:26 -08:00
|
|
|
)
|
2014-02-25 12:41:31 -08:00
|
|
|
if err := d.createContainerRoot(c.ID); err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
2014-02-25 16:27:07 -08:00
|
|
|
defer d.removeContainerRoot(c.ID)
|
|
|
|
|
2014-04-30 18:20:01 -07:00
|
|
|
if err := d.writeContainerFile(container, c.ID); err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
|
|
|
|
term := getTerminal(c, pipes)
|
|
|
|
|
2014-04-30 18:49:24 -07:00
|
|
|
return nsinit.Exec(container, term, c.Rootfs, dataPath, args, func(container *libcontainer.Container, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd {
|
|
|
|
// we need to join the rootfs because nsinit will setup the rootfs and chroot
|
|
|
|
initPath := filepath.Join(c.Rootfs, c.InitPath)
|
|
|
|
|
|
|
|
c.Path = d.initPath
|
|
|
|
c.Args = append([]string{
|
|
|
|
initPath,
|
|
|
|
"-driver", DriverName,
|
|
|
|
"-console", console,
|
|
|
|
"-pipe", "3",
|
|
|
|
"-root", filepath.Join(d.root, c.ID),
|
|
|
|
"--",
|
|
|
|
}, args...)
|
|
|
|
|
|
|
|
// set this to nil so that when we set the clone flags anything else is reset
|
|
|
|
c.SysProcAttr = nil
|
|
|
|
system.SetCloneFlags(&c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
|
|
|
|
c.ExtraFiles = []*os.File{child}
|
|
|
|
|
|
|
|
c.Env = container.Env
|
|
|
|
c.Dir = c.Rootfs
|
|
|
|
|
|
|
|
return &c.Cmd
|
|
|
|
}, func() {
|
|
|
|
if startCallback != nil {
|
2014-05-06 17:04:04 -07:00
|
|
|
c.ContainerPid = c.Process.Pid
|
2014-04-30 18:49:24 -07:00
|
|
|
startCallback(c)
|
2014-04-30 15:52:40 -07:00
|
|
|
}
|
2014-04-30 18:49:24 -07:00
|
|
|
})
|
2014-02-21 17:11:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
2014-03-26 06:48:16 +00:00
|
|
|
return syscall.Kill(p.Process.Pid, syscall.Signal(sig))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Terminate(p *execdriver.Command) error {
|
|
|
|
// lets check the start time for the process
|
|
|
|
started, err := d.readStartTime(p)
|
|
|
|
if err != nil {
|
|
|
|
// if we don't have the data on disk then we can assume the process is gone
|
|
|
|
// because this is only removed after we know the process has stopped
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
currentStartTime, err := system.GetProcessStartTime(p.Process.Pid)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if started == currentStartTime {
|
|
|
|
err = syscall.Kill(p.Process.Pid, 9)
|
|
|
|
}
|
2014-03-06 14:14:25 -08:00
|
|
|
d.removeContainerRoot(p.ID)
|
|
|
|
return err
|
2014-03-26 06:48:16 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) readStartTime(p *execdriver.Command) (string, error) {
|
|
|
|
data, err := ioutil.ReadFile(filepath.Join(d.root, p.ID, "start"))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(data), nil
|
2014-02-21 17:11:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Info(id string) execdriver.Info {
|
2014-02-24 14:11:09 -08:00
|
|
|
return &info{
|
|
|
|
ID: id,
|
|
|
|
driver: d,
|
|
|
|
}
|
2014-02-21 17:11:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Name() string {
|
|
|
|
return fmt.Sprintf("%s-%s", DriverName, Version)
|
|
|
|
}
|
|
|
|
|
2014-02-24 21:11:52 -08:00
|
|
|
// TODO: this can be improved with our driver
|
|
|
|
// there has to be a better way to do this
|
2014-02-21 17:11:57 -08:00
|
|
|
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
2014-02-24 15:47:23 -08:00
|
|
|
pids := []int{}
|
|
|
|
|
2014-02-24 16:26:06 -08:00
|
|
|
subsystem := "devices"
|
2014-02-24 15:47:23 -08:00
|
|
|
cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
|
|
|
|
if err != nil {
|
|
|
|
return pids, err
|
|
|
|
}
|
|
|
|
cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
|
|
|
|
if err != nil {
|
|
|
|
return pids, err
|
|
|
|
}
|
|
|
|
|
|
|
|
filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
|
|
|
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
|
|
filename = filepath.Join(cgroupRoot, cgroupDir, "docker", id, "tasks")
|
|
|
|
}
|
|
|
|
|
|
|
|
output, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
return pids, err
|
|
|
|
}
|
|
|
|
for _, p := range strings.Split(string(output), "\n") {
|
|
|
|
if len(p) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pid, err := strconv.Atoi(p)
|
|
|
|
if err != nil {
|
|
|
|
return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
|
|
|
|
}
|
|
|
|
pids = append(pids, pid)
|
|
|
|
}
|
|
|
|
return pids, nil
|
2014-02-21 17:11:57 -08:00
|
|
|
}
|
|
|
|
|
2014-02-25 12:41:31 -08:00
|
|
|
func (d *driver) writeContainerFile(container *libcontainer.Container, id string) error {
|
2014-02-21 17:11:57 -08:00
|
|
|
data, err := json.Marshal(container)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-02-25 12:41:31 -08:00
|
|
|
return ioutil.WriteFile(filepath.Join(d.root, id, "container.json"), data, 0655)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) createContainerRoot(id string) error {
|
|
|
|
return os.MkdirAll(filepath.Join(d.root, id), 0655)
|
2014-02-21 17:11:57 -08:00
|
|
|
}
|
|
|
|
|
2014-02-25 16:27:07 -08:00
|
|
|
func (d *driver) removeContainerRoot(id string) error {
|
|
|
|
return os.RemoveAll(filepath.Join(d.root, id))
|
|
|
|
}
|
|
|
|
|
2014-02-21 17:11:57 -08:00
|
|
|
func getEnv(key string, env []string) string {
|
|
|
|
for _, pair := range env {
|
|
|
|
parts := strings.Split(pair, "=")
|
|
|
|
if parts[0] == key {
|
|
|
|
return parts[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2014-04-30 18:20:01 -07:00
|
|
|
func getTerminal(c *execdriver.Command, pipes *execdriver.Pipes) nsinit.Terminal {
|
|
|
|
var term nsinit.Terminal
|
|
|
|
if c.Tty {
|
|
|
|
term = &dockerTtyTerm{
|
|
|
|
pipes: pipes,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
term = &dockerStdTerm{
|
|
|
|
pipes: pipes,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.Terminal = term
|
|
|
|
return term
|
2014-02-21 17:11:57 -08:00
|
|
|
}
|