summaryrefslogtreecommitdiff
path: root/runtime/execdriver/native
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/execdriver/native')
-rw-r--r--runtime/execdriver/native/configuration/parse.go186
-rw-r--r--runtime/execdriver/native/configuration/parse_test.go166
-rw-r--r--runtime/execdriver/native/create.go114
-rw-r--r--runtime/execdriver/native/driver.go292
-rw-r--r--runtime/execdriver/native/info.go21
-rw-r--r--runtime/execdriver/native/template/default_template.go45
-rw-r--r--runtime/execdriver/native/term.go42
7 files changed, 866 insertions, 0 deletions
diff --git a/runtime/execdriver/native/configuration/parse.go b/runtime/execdriver/native/configuration/parse.go
new file mode 100644
index 0000000000..6d6c643919
--- /dev/null
+++ b/runtime/execdriver/native/configuration/parse.go
@@ -0,0 +1,186 @@
+package configuration
+
+import (
+ "fmt"
+ "github.com/dotcloud/docker/pkg/libcontainer"
+ "github.com/dotcloud/docker/utils"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+type Action func(*libcontainer.Container, interface{}, string) error
+
+var actions = map[string]Action{
+ "cap.add": addCap, // add a cap
+ "cap.drop": dropCap, // drop a cap
+
+ "ns.add": addNamespace, // add a namespace
+ "ns.drop": dropNamespace, // drop a namespace when cloning
+
+ "net.join": joinNetNamespace, // join another containers net namespace
+
+ "cgroups.cpu_shares": cpuShares, // set the cpu shares
+ "cgroups.memory": memory, // set the memory limit
+ "cgroups.memory_swap": memorySwap, // set the memory swap limit
+ "cgroups.cpuset.cpus": cpusetCpus, // set the cpus used
+
+ "apparmor_profile": apparmorProfile, // set the apparmor profile to apply
+
+ "fs.readonly": readonlyFs, // make the rootfs of the container read only
+}
+
+func cpusetCpus(container *libcontainer.Container, context interface{}, value string) error {
+ if container.Cgroups == nil {
+ return fmt.Errorf("cannot set cgroups when they are disabled")
+ }
+ container.Cgroups.CpusetCpus = value
+
+ return nil
+}
+
+func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error {
+ container.Context["apparmor_profile"] = value
+ return nil
+}
+
+func cpuShares(container *libcontainer.Container, context interface{}, value string) error {
+ if container.Cgroups == nil {
+ return fmt.Errorf("cannot set cgroups when they are disabled")
+ }
+ v, err := strconv.ParseInt(value, 10, 0)
+ if err != nil {
+ return err
+ }
+ container.Cgroups.CpuShares = v
+ return nil
+}
+
+func memory(container *libcontainer.Container, context interface{}, value string) error {
+ if container.Cgroups == nil {
+ return fmt.Errorf("cannot set cgroups when they are disabled")
+ }
+
+ v, err := utils.RAMInBytes(value)
+ if err != nil {
+ return err
+ }
+ container.Cgroups.Memory = v
+ return nil
+}
+
+func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
+ if container.Cgroups == nil {
+ return fmt.Errorf("cannot set cgroups when they are disabled")
+ }
+ v, err := strconv.ParseInt(value, 0, 64)
+ if err != nil {
+ return err
+ }
+ container.Cgroups.MemorySwap = v
+ return nil
+}
+
+func addCap(container *libcontainer.Container, context interface{}, value string) error {
+ c := container.CapabilitiesMask.Get(value)
+ if c == nil {
+ return fmt.Errorf("%s is not a valid capability", value)
+ }
+ c.Enabled = true
+ return nil
+}
+
+func dropCap(container *libcontainer.Container, context interface{}, value string) error {
+ c := container.CapabilitiesMask.Get(value)
+ if c == nil {
+ return fmt.Errorf("%s is not a valid capability", value)
+ }
+ c.Enabled = false
+ return nil
+}
+
+func addNamespace(container *libcontainer.Container, context interface{}, value string) error {
+ ns := container.Namespaces.Get(value)
+ if ns == nil {
+ return fmt.Errorf("%s is not a valid namespace", value[1:])
+ }
+ ns.Enabled = true
+ return nil
+}
+
+func dropNamespace(container *libcontainer.Container, context interface{}, value string) error {
+ ns := container.Namespaces.Get(value)
+ if ns == nil {
+ return fmt.Errorf("%s is not a valid namespace", value[1:])
+ }
+ ns.Enabled = false
+ return nil
+}
+
+func readonlyFs(container *libcontainer.Container, context interface{}, value string) error {
+ switch value {
+ case "1", "true":
+ container.ReadonlyFs = true
+ default:
+ container.ReadonlyFs = false
+ }
+ return nil
+}
+
+func joinNetNamespace(container *libcontainer.Container, context interface{}, value string) error {
+ var (
+ running = context.(map[string]*exec.Cmd)
+ cmd = running[value]
+ )
+
+ if cmd == nil || cmd.Process == nil {
+ return fmt.Errorf("%s is not a valid running container to join", value)
+ }
+ nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
+ container.Networks = append(container.Networks, &libcontainer.Network{
+ Type: "netns",
+ Context: libcontainer.Context{
+ "nspath": nspath,
+ },
+ })
+ return nil
+}
+
+func vethMacAddress(container *libcontainer.Container, context interface{}, value string) error {
+ var veth *libcontainer.Network
+ for _, network := range container.Networks {
+ if network.Type == "veth" {
+ veth = network
+ break
+ }
+ }
+ if veth == nil {
+ return fmt.Errorf("not veth configured for container")
+ }
+ veth.Context["mac"] = value
+ return nil
+}
+
+// configureCustomOptions takes string commands from the user and allows modification of the
+// container's default configuration.
+//
+// TODO: this can be moved to a general utils or parser in pkg
+func ParseConfiguration(container *libcontainer.Container, running map[string]*exec.Cmd, opts []string) error {
+ for _, opt := range opts {
+ kv := strings.SplitN(opt, "=", 2)
+ if len(kv) < 2 {
+ return fmt.Errorf("invalid format for %s", opt)
+ }
+
+ action, exists := actions[kv[0]]
+ if !exists {
+ return fmt.Errorf("%s is not a valid option for the native driver", kv[0])
+ }
+
+ if err := action(container, running, kv[1]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/runtime/execdriver/native/configuration/parse_test.go b/runtime/execdriver/native/configuration/parse_test.go
new file mode 100644
index 0000000000..8001358766
--- /dev/null
+++ b/runtime/execdriver/native/configuration/parse_test.go
@@ -0,0 +1,166 @@
+package configuration
+
+import (
+ "github.com/dotcloud/docker/runtime/execdriver/native/template"
+ "testing"
+)
+
+func TestSetReadonlyRootFs(t *testing.T) {
+ var (
+ container = template.New()
+ opts = []string{
+ "fs.readonly=true",
+ }
+ )
+
+ if container.ReadonlyFs {
+ t.Fatal("container should not have a readonly rootfs by default")
+ }
+ if err := ParseConfiguration(container, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+
+ if !container.ReadonlyFs {
+ t.Fatal("container should have a readonly rootfs")
+ }
+}
+
+func TestConfigurationsDoNotConflict(t *testing.T) {
+ var (
+ container1 = template.New()
+ container2 = template.New()
+ opts = []string{
+ "cap.add=NET_ADMIN",
+ }
+ )
+
+ if err := ParseConfiguration(container1, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+
+ if !container1.CapabilitiesMask.Get("NET_ADMIN").Enabled {
+ t.Fatal("container one should have NET_ADMIN enabled")
+ }
+ if container2.CapabilitiesMask.Get("NET_ADMIN").Enabled {
+ t.Fatal("container two should not have NET_ADMIN enabled")
+ }
+}
+
+func TestCpusetCpus(t *testing.T) {
+ var (
+ container = template.New()
+ opts = []string{
+ "cgroups.cpuset.cpus=1,2",
+ }
+ )
+ if err := ParseConfiguration(container, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+
+ if expected := "1,2"; container.Cgroups.CpusetCpus != expected {
+ t.Fatalf("expected %s got %s for cpuset.cpus", expected, container.Cgroups.CpusetCpus)
+ }
+}
+
+func TestAppArmorProfile(t *testing.T) {
+ var (
+ container = template.New()
+ opts = []string{
+ "apparmor_profile=koye-the-protector",
+ }
+ )
+ if err := ParseConfiguration(container, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+ if expected := "koye-the-protector"; container.Context["apparmor_profile"] != expected {
+ t.Fatalf("expected profile %s got %s", expected, container.Context["apparmor_profile"])
+ }
+}
+
+func TestCpuShares(t *testing.T) {
+ var (
+ container = template.New()
+ opts = []string{
+ "cgroups.cpu_shares=1048",
+ }
+ )
+ if err := ParseConfiguration(container, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+
+ if expected := int64(1048); container.Cgroups.CpuShares != expected {
+ t.Fatalf("expected cpu shares %d got %d", expected, container.Cgroups.CpuShares)
+ }
+}
+
+func TestCgroupMemory(t *testing.T) {
+ var (
+ container = template.New()
+ opts = []string{
+ "cgroups.memory=500m",
+ }
+ )
+ if err := ParseConfiguration(container, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+
+ if expected := int64(500 * 1024 * 1024); container.Cgroups.Memory != expected {
+ t.Fatalf("expected memory %d got %d", expected, container.Cgroups.Memory)
+ }
+}
+
+func TestAddCap(t *testing.T) {
+ var (
+ container = template.New()
+ opts = []string{
+ "cap.add=MKNOD",
+ "cap.add=SYS_ADMIN",
+ }
+ )
+ if err := ParseConfiguration(container, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+
+ if !container.CapabilitiesMask.Get("MKNOD").Enabled {
+ t.Fatal("container should have MKNOD enabled")
+ }
+ if !container.CapabilitiesMask.Get("SYS_ADMIN").Enabled {
+ t.Fatal("container should have SYS_ADMIN enabled")
+ }
+}
+
+func TestDropCap(t *testing.T) {
+ var (
+ container = template.New()
+ opts = []string{
+ "cap.drop=MKNOD",
+ }
+ )
+ // enabled all caps like in privileged mode
+ for _, c := range container.CapabilitiesMask {
+ c.Enabled = true
+ }
+ if err := ParseConfiguration(container, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+
+ if container.CapabilitiesMask.Get("MKNOD").Enabled {
+ t.Fatal("container should not have MKNOD enabled")
+ }
+}
+
+func TestDropNamespace(t *testing.T) {
+ var (
+ container = template.New()
+ opts = []string{
+ "ns.drop=NEWNET",
+ }
+ )
+ if err := ParseConfiguration(container, nil, opts); err != nil {
+ t.Fatal(err)
+ }
+
+ if container.Namespaces.Get("NEWNET").Enabled {
+ t.Fatal("container should not have NEWNET enabled")
+ }
+}
diff --git a/runtime/execdriver/native/create.go b/runtime/execdriver/native/create.go
new file mode 100644
index 0000000000..71fab3e064
--- /dev/null
+++ b/runtime/execdriver/native/create.go
@@ -0,0 +1,114 @@
+package native
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/dotcloud/docker/pkg/label"
+ "github.com/dotcloud/docker/pkg/libcontainer"
+ "github.com/dotcloud/docker/runtime/execdriver"
+ "github.com/dotcloud/docker/runtime/execdriver/native/configuration"
+ "github.com/dotcloud/docker/runtime/execdriver/native/template"
+)
+
+// createContainer populates and configures the container type with the
+// data provided by the execdriver.Command
+func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container, error) {
+ container := template.New()
+
+ container.Hostname = getEnv("HOSTNAME", c.Env)
+ container.Tty = c.Tty
+ container.User = c.User
+ container.WorkingDir = c.WorkingDir
+ container.Env = c.Env
+ container.Cgroups.Name = c.ID
+ // check to see if we are running in ramdisk to disable pivot root
+ container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
+
+ if err := d.createNetwork(container, c); err != nil {
+ return nil, err
+ }
+ if c.Privileged {
+ if err := d.setPrivileged(container); err != nil {
+ return nil, err
+ }
+ }
+ if err := d.setupCgroups(container, c); err != nil {
+ return nil, err
+ }
+ if err := d.setupMounts(container, c); err != nil {
+ return nil, err
+ }
+ if err := d.setupLabels(container, c); err != nil {
+ return nil, err
+ }
+ if err := configuration.ParseConfiguration(container, d.activeContainers, c.Config["native"]); err != nil {
+ return nil, err
+ }
+ return container, nil
+}
+
+func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
+ container.Networks = []*libcontainer.Network{
+ {
+ Mtu: c.Network.Mtu,
+ Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
+ Gateway: "localhost",
+ Type: "loopback",
+ Context: libcontainer.Context{},
+ },
+ }
+
+ if c.Network.Interface != nil {
+ vethNetwork := libcontainer.Network{
+ Mtu: c.Network.Mtu,
+ Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
+ Gateway: c.Network.Interface.Gateway,
+ Type: "veth",
+ Context: libcontainer.Context{
+ "prefix": "veth",
+ "bridge": c.Network.Interface.Bridge,
+ },
+ }
+ container.Networks = append(container.Networks, &vethNetwork)
+ }
+ return nil
+}
+
+func (d *driver) setPrivileged(container *libcontainer.Container) error {
+ for _, c := range container.CapabilitiesMask {
+ c.Enabled = true
+ }
+ container.Cgroups.DeviceAccess = true
+ container.Context["apparmor_profile"] = "unconfined"
+ return nil
+}
+
+func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.Command) error {
+ if c.Resources != nil {
+ container.Cgroups.CpuShares = c.Resources.CpuShares
+ container.Cgroups.Memory = c.Resources.Memory
+ container.Cgroups.MemorySwap = c.Resources.MemorySwap
+ }
+ return nil
+}
+
+func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error {
+ for _, m := range c.Mounts {
+ container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
+ }
+ return nil
+}
+
+func (d *driver) setupLabels(container *libcontainer.Container, c *execdriver.Command) error {
+ labels := c.Config["label"]
+ if len(labels) > 0 {
+ process, mount, err := label.GenLabels(labels[0])
+ if err != nil {
+ return err
+ }
+ container.Context["mount_label"] = mount
+ container.Context["process_label"] = process
+ }
+ return nil
+}
diff --git a/runtime/execdriver/native/driver.go b/runtime/execdriver/native/driver.go
new file mode 100644
index 0000000000..d18865e508
--- /dev/null
+++ b/runtime/execdriver/native/driver.go
@@ -0,0 +1,292 @@
+package native
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/dotcloud/docker/pkg/cgroups"
+ "github.com/dotcloud/docker/pkg/libcontainer"
+ "github.com/dotcloud/docker/pkg/libcontainer/apparmor"
+ "github.com/dotcloud/docker/pkg/libcontainer/nsinit"
+ "github.com/dotcloud/docker/pkg/system"
+ "github.com/dotcloud/docker/runtime/execdriver"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+)
+
+const (
+ DriverName = "native"
+ Version = "0.1"
+ BackupApparmorProfilePath = "apparmor/docker.back" // relative to docker root
+)
+
+func init() {
+ execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
+ var (
+ container *libcontainer.Container
+ ns = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
+ )
+ f, err := os.Open(filepath.Join(args.Root, "container.json"))
+ if err != nil {
+ return err
+ }
+ if err := json.NewDecoder(f).Decode(&container); err != nil {
+ f.Close()
+ return err
+ }
+ f.Close()
+
+ cwd, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+ syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(args.Pipe))
+ if err != nil {
+ return err
+ }
+ if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
+ return err
+ }
+ return nil
+ })
+}
+
+type driver struct {
+ root string
+ initPath string
+ activeContainers map[string]*exec.Cmd
+}
+
+func NewDriver(root, initPath string) (*driver, error) {
+ if err := os.MkdirAll(root, 0700); err != nil {
+ return nil, err
+ }
+ // native driver root is at docker_root/execdriver/native. Put apparmor at docker_root
+ if err := apparmor.InstallDefaultProfile(filepath.Join(root, "../..", BackupApparmorProfilePath)); err != nil {
+ return nil, err
+ }
+ return &driver{
+ root: root,
+ initPath: initPath,
+ activeContainers: make(map[string]*exec.Cmd),
+ }, nil
+}
+
+func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
+ // 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
+
+ var (
+ term nsinit.Terminal
+ factory = &dockerCommandFactory{c: c, driver: d}
+ stateWriter = &dockerStateWriter{
+ callback: startCallback,
+ c: c,
+ dsw: &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
+ }
+ ns = nsinit.NewNsInit(factory, stateWriter, createLogger(os.Getenv("DEBUG")))
+ args = append([]string{c.Entrypoint}, c.Arguments...)
+ )
+ if err := d.createContainerRoot(c.ID); err != nil {
+ return -1, err
+ }
+ defer d.removeContainerRoot(c.ID)
+
+ if c.Tty {
+ term = &dockerTtyTerm{
+ pipes: pipes,
+ }
+ } else {
+ term = &dockerStdTerm{
+ pipes: pipes,
+ }
+ }
+ c.Terminal = term
+ if err := d.writeContainerFile(container, c.ID); err != nil {
+ return -1, err
+ }
+ return ns.Exec(container, term, args)
+}
+
+func (d *driver) Kill(p *execdriver.Command, sig int) error {
+ 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)
+ }
+ d.removeContainerRoot(p.ID)
+ return err
+
+}
+
+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
+}
+
+func (d *driver) Info(id string) execdriver.Info {
+ return &info{
+ ID: id,
+ driver: d,
+ }
+}
+
+func (d *driver) Name() string {
+ return fmt.Sprintf("%s-%s", DriverName, Version)
+}
+
+// TODO: this can be improved with our driver
+// there has to be a better way to do this
+func (d *driver) GetPidsForContainer(id string) ([]int, error) {
+ pids := []int{}
+
+ subsystem := "devices"
+ 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
+}
+
+func (d *driver) writeContainerFile(container *libcontainer.Container, id string) error {
+ data, err := json.Marshal(container)
+ if err != nil {
+ return err
+ }
+ 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)
+}
+
+func (d *driver) removeContainerRoot(id string) error {
+ return os.RemoveAll(filepath.Join(d.root, id))
+}
+
+func getEnv(key string, env []string) string {
+ for _, pair := range env {
+ parts := strings.Split(pair, "=")
+ if parts[0] == key {
+ return parts[1]
+ }
+ }
+ return ""
+}
+
+type dockerCommandFactory struct {
+ c *execdriver.Command
+ driver *driver
+}
+
+// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
+// defined on the container's configuration and use the current binary as the init with the
+// args provided
+func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
+ // we need to join the rootfs because nsinit will setup the rootfs and chroot
+ initPath := filepath.Join(d.c.Rootfs, d.c.InitPath)
+
+ d.c.Path = d.driver.initPath
+ d.c.Args = append([]string{
+ initPath,
+ "-driver", DriverName,
+ "-console", console,
+ "-pipe", "3",
+ "-root", filepath.Join(d.driver.root, d.c.ID),
+ "--",
+ }, args...)
+
+ // set this to nil so that when we set the clone flags anything else is reset
+ d.c.SysProcAttr = nil
+ system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
+ d.c.ExtraFiles = []*os.File{syncFile}
+
+ d.c.Env = container.Env
+ d.c.Dir = d.c.Rootfs
+
+ return &d.c.Cmd
+}
+
+type dockerStateWriter struct {
+ dsw nsinit.StateWriter
+ c *execdriver.Command
+ callback execdriver.StartCallback
+}
+
+func (d *dockerStateWriter) WritePid(pid int, started string) error {
+ d.c.ContainerPid = pid
+ err := d.dsw.WritePid(pid, started)
+ if d.callback != nil {
+ d.callback(d.c)
+ }
+ return err
+}
+
+func (d *dockerStateWriter) DeletePid() error {
+ return d.dsw.DeletePid()
+}
+
+func createLogger(debug string) *log.Logger {
+ var w io.Writer
+ // if we are in debug mode set the logger to stderr
+ if debug != "" {
+ w = os.Stderr
+ } else {
+ w = ioutil.Discard
+ }
+ return log.New(w, "[libcontainer] ", log.LstdFlags)
+}
diff --git a/runtime/execdriver/native/info.go b/runtime/execdriver/native/info.go
new file mode 100644
index 0000000000..aef2f85c6b
--- /dev/null
+++ b/runtime/execdriver/native/info.go
@@ -0,0 +1,21 @@
+package native
+
+import (
+ "os"
+ "path/filepath"
+)
+
+type info struct {
+ ID string
+ driver *driver
+}
+
+// IsRunning is determined by looking for the
+// pid file for a container. If the file exists then the
+// container is currently running
+func (i *info) IsRunning() bool {
+ if _, err := os.Stat(filepath.Join(i.driver.root, i.ID, "pid")); err == nil {
+ return true
+ }
+ return false
+}
diff --git a/runtime/execdriver/native/template/default_template.go b/runtime/execdriver/native/template/default_template.go
new file mode 100644
index 0000000000..a1ecb04d76
--- /dev/null
+++ b/runtime/execdriver/native/template/default_template.go
@@ -0,0 +1,45 @@
+package template
+
+import (
+ "github.com/dotcloud/docker/pkg/cgroups"
+ "github.com/dotcloud/docker/pkg/libcontainer"
+)
+
+// New returns the docker default configuration for libcontainer
+func New() *libcontainer.Container {
+ container := &libcontainer.Container{
+ CapabilitiesMask: libcontainer.Capabilities{
+ libcontainer.GetCapability("SETPCAP"),
+ libcontainer.GetCapability("SYS_MODULE"),
+ libcontainer.GetCapability("SYS_RAWIO"),
+ libcontainer.GetCapability("SYS_PACCT"),
+ libcontainer.GetCapability("SYS_ADMIN"),
+ libcontainer.GetCapability("SYS_NICE"),
+ libcontainer.GetCapability("SYS_RESOURCE"),
+ libcontainer.GetCapability("SYS_TIME"),
+ libcontainer.GetCapability("SYS_TTY_CONFIG"),
+ libcontainer.GetCapability("AUDIT_WRITE"),
+ libcontainer.GetCapability("AUDIT_CONTROL"),
+ libcontainer.GetCapability("MAC_OVERRIDE"),
+ libcontainer.GetCapability("MAC_ADMIN"),
+ libcontainer.GetCapability("NET_ADMIN"),
+ libcontainer.GetCapability("MKNOD"),
+ },
+ Namespaces: libcontainer.Namespaces{
+ libcontainer.GetNamespace("NEWNS"),
+ libcontainer.GetNamespace("NEWUTS"),
+ libcontainer.GetNamespace("NEWIPC"),
+ libcontainer.GetNamespace("NEWPID"),
+ libcontainer.GetNamespace("NEWNET"),
+ },
+ Cgroups: &cgroups.Cgroup{
+ Parent: "docker",
+ DeviceAccess: false,
+ },
+ Context: libcontainer.Context{
+ "apparmor_profile": "docker-default",
+ },
+ }
+ container.CapabilitiesMask.Get("MKNOD").Enabled = true
+ return container
+}
diff --git a/runtime/execdriver/native/term.go b/runtime/execdriver/native/term.go
new file mode 100644
index 0000000000..0d5298d388
--- /dev/null
+++ b/runtime/execdriver/native/term.go
@@ -0,0 +1,42 @@
+/*
+ These types are wrappers around the libcontainer Terminal interface so that
+ we can resuse the docker implementations where possible.
+*/
+package native
+
+import (
+ "github.com/dotcloud/docker/runtime/execdriver"
+ "io"
+ "os"
+ "os/exec"
+)
+
+type dockerStdTerm struct {
+ execdriver.StdConsole
+ pipes *execdriver.Pipes
+}
+
+func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
+ return d.AttachPipes(cmd, d.pipes)
+}
+
+func (d *dockerStdTerm) SetMaster(master *os.File) {
+ // do nothing
+}
+
+type dockerTtyTerm struct {
+ execdriver.TtyConsole
+ pipes *execdriver.Pipes
+}
+
+func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
+ go io.Copy(t.pipes.Stdout, t.MasterPty)
+ if t.pipes.Stdin != nil {
+ go io.Copy(t.MasterPty, t.pipes.Stdin)
+ }
+ return nil
+}
+
+func (t *dockerTtyTerm) SetMaster(master *os.File) {
+ t.MasterPty = master
+}