diff --git a/container.go b/container.go index d5dbddb773..549449080c 100644 --- a/container.go +++ b/container.go @@ -747,6 +747,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { } params := []string{ + "lxc-start", "-n", container.ID, "-f", container.lxcConfigPath(), "--", @@ -795,7 +796,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 +}