Преглед изворни кода

Add execin function to running a process in a namespace
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby пре 11 година
родитељ
комит
420b5eb211

+ 1 - 1
pkg/libcontainer/container.json

@@ -1,5 +1,5 @@
 {
-    "id": "koye",
+    "hostname": "koye",
     "command": {
         "args": [
             "/bin/bash"

+ 115 - 0
pkg/libcontainer/nsinit/execin.go

@@ -0,0 +1,115 @@
+package main
+
+import (
+	"fmt"
+	"github.com/dotcloud/docker/pkg/libcontainer"
+	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
+	"github.com/dotcloud/docker/pkg/system"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
+	"syscall"
+)
+
+func execinCommand(container *libcontainer.Container) (int, error) {
+	nspid, err := readPid()
+	if err != nil {
+		return -1, err
+	}
+
+	for _, ns := range container.Namespaces {
+		if err := system.Unshare(namespaceMap[ns]); err != nil {
+			return -1, err
+		}
+	}
+	fds, err := getNsFds(nspid, container)
+	closeFds := func() {
+		for _, f := range fds {
+			system.Closefd(f)
+		}
+	}
+	if err != nil {
+		closeFds()
+		return -1, err
+	}
+
+	for _, fd := range fds {
+		if fd > 0 {
+			if err := system.Setns(fd, 0); err != nil {
+				closeFds()
+				return -1, fmt.Errorf("setns %s", err)
+			}
+		}
+		system.Closefd(fd)
+	}
+
+	// if the container has a new pid and mount namespace we need to
+	// remount proc and sys to pick up the changes
+	if container.Namespaces.Contains(libcontainer.CLONE_NEWNS) &&
+		container.Namespaces.Contains(libcontainer.CLONE_NEWPID) {
+
+		pid, err := system.Fork()
+		if err != nil {
+			return -1, err
+		}
+		if pid == 0 {
+			// TODO: make all raw syscalls to be fork safe
+			if err := system.Unshare(syscall.CLONE_NEWNS); err != nil {
+				return -1, err
+			}
+			if err := remountProc(); err != nil {
+				return -1, fmt.Errorf("remount proc %s", err)
+			}
+			if err := remountSys(); err != nil {
+				return -1, fmt.Errorf("remount sys %s", err)
+			}
+			if err := capabilities.DropCapabilities(container); err != nil {
+				return -1, fmt.Errorf("drop capabilities %s", err)
+			}
+			if err := system.Exec(container.Command.Args[0], container.Command.Args[0:], container.Command.Env); err != nil {
+				return -1, err
+			}
+		}
+		proc, err := os.FindProcess(pid)
+		if err != nil {
+			return -1, err
+		}
+		state, err := proc.Wait()
+		if err != nil {
+			return -1, err
+		}
+		os.Exit(state.Sys().(syscall.WaitStatus).ExitStatus())
+	}
+	if err := capabilities.DropCapabilities(container); err != nil {
+		return -1, fmt.Errorf("drop capabilities %s", err)
+	}
+	if err := system.Exec(container.Command.Args[0], container.Command.Args[0:], container.Command.Env); err != nil {
+		return -1, err
+	}
+	panic("unreachable")
+}
+
+func readPid() (int, error) {
+	data, err := ioutil.ReadFile(".nspid")
+	if err != nil {
+		return -1, err
+	}
+	pid, err := strconv.Atoi(string(data))
+	if err != nil {
+		return -1, err
+	}
+	return pid, nil
+}
+
+func getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
+	fds := make([]uintptr, len(container.Namespaces))
+	for i, ns := range container.Namespaces {
+		f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", namespaceFileMap[ns]), os.O_RDONLY, 0)
+		if err != nil {
+			return fds, err
+		}
+		fds[i] = f.Fd()
+	}
+	return fds, nil
+}

+ 8 - 0
pkg/libcontainer/nsinit/main.go

@@ -37,6 +37,14 @@ func main() {
 		if err := initCommand(container, os.Args[2]); err != nil {
 			log.Fatal(err)
 		}
+	case "execin":
+		exitCode, err := execinCommand(container)
+		if err != nil {
+			log.Fatal(err)
+		}
+		os.Exit(exitCode)
+	default:
+		log.Fatalf("command not supported for nsinit %s", os.Args[1])
 	}
 }
 

+ 9 - 0
pkg/libcontainer/nsinit/ns_linux.go

@@ -14,6 +14,15 @@ var namespaceMap = map[libcontainer.Namespace]int{
 	libcontainer.CLONE_NEWNET:  syscall.CLONE_NEWNET,
 }
 
+var namespaceFileMap = map[libcontainer.Namespace]string{
+	libcontainer.CLONE_NEWNS:   "mnt",
+	libcontainer.CLONE_NEWUTS:  "uts",
+	libcontainer.CLONE_NEWIPC:  "ipc",
+	libcontainer.CLONE_NEWUSER: "user",
+	libcontainer.CLONE_NEWPID:  "pid",
+	libcontainer.CLONE_NEWNET:  "net",
+}
+
 // getNamespaceFlags parses the container's Namespaces options to set the correct
 // flags on clone, unshare, and setns
 func getNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {