From 429587779a95a4d38ec9cd66202de9729c320ef8 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 12 Sep 2013 15:18:11 +0200 Subject: [PATCH] lxc: Work around lxc-start need for private mounts lxc-start requires / to be mounted private, otherwise the changes it does inside the container (both mounts and unmounts) will propagate out to the host. We work around this by starting up lxc-start in its own namespace where we set / to rprivate. Unfortunately go can't really execute any code between clone and exec, so we can't do this in a nice way. Instead we have a horrible hack that use the unshare command, the shell and the mount command... --- container.go | 17 ++++++++++++++++- utils.go | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/container.go b/container.go index 002e2eb31e..35060e873e 100644 --- a/container.go +++ b/container.go @@ -748,6 +748,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { } params := []string{ + "lxc-start", "-n", container.ID, "-f", container.lxcConfigPath(), "--", @@ -796,7 +797,21 @@ func (container *Container) Start(hostConfig *HostConfig) error { params = append(params, "--", container.Path) params = append(params, container.Args...) - container.cmd = exec.Command("lxc-start", params...) + if RootIsShared() { + // lxc-start really needs / to be private, or all kinds of stuff break + // What we really want is to clone into a new namespace and then + // mount / MS_REC|MS_PRIVATE, but since we can't really clone or fork + // without exec in go we have to do this horrible shell hack... + shellString := + "mount --make-rprivate /; exec " + + utils.ShellQuoteArguments(params) + + params = []string{ + "unshare", "-m", "--", "/bin/sh", "-c", shellString, + } + } + + container.cmd = exec.Command(params[0], params[1:]...) // Setup logging of stdout and stderr to disk if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { diff --git a/utils.go b/utils.go index aed8ffdd76..babca65bf1 100644 --- a/utils.go +++ b/utils.go @@ -2,6 +2,7 @@ package docker import ( "fmt" + "io/ioutil" "strings" ) @@ -167,3 +168,17 @@ func parseLxcOpt(opt string) (string, string, error) { } return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil } + +func RootIsShared() bool { + if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil { + for _, line := range strings.Split(string(data), "\n") { + cols := strings.Split(line, " ") + if cols[3] == "/" && cols[4] == "/" { + return strings.HasPrefix(cols[6], "shared") + } + } + } + + // No idea, probably safe to assume so + return true +}