diff --git a/namespaces/exec.go b/namespaces/exec.go index c4b724a78..e32da4b86 100644 --- a/namespaces/exec.go +++ b/namespaces/exec.go @@ -32,6 +32,7 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string if err != nil { return -1, err } + defer syncPipe.Close() if container.Tty { master, console, err = system.CreateMasterAndConsole() @@ -52,6 +53,9 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string return -1, err } + // Now we passed the pipe to the child, close our side + syncPipe.CloseChild() + started, err := system.GetProcessStartTime(command.Process.Pid) if err != nil { return -1, err @@ -90,7 +94,9 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string defer libcontainer.DeleteState(dataPath) // Sync with child - syncPipe.Close() + if err := syncPipe.BlockOnChild(); err != nil { + return -1, err + } if startCallback != nil { startCallback() diff --git a/namespaces/init.go b/namespaces/init.go index 6c4745142..4a6ce6e3a 100644 --- a/namespaces/init.go +++ b/namespaces/init.go @@ -27,7 +27,13 @@ import ( // Move this to libcontainer package. // Init is the init process that first runs inside a new namespace to setup mounts, users, networking, // and other options required for the new container. -func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error { +func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) (err error) { + defer func() { + if err != nil { + syncPipe.ReportError(err) + } + }() + rootfs, err := utils.ResolveRootfs(uncleanRootfs) if err != nil { return err @@ -42,10 +48,8 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn // We always read this as it is a way to sync with the parent as well networkState, err := syncPipe.ReadFromParent() if err != nil { - syncPipe.Close() return err } - syncPipe.Close() if consolePath != "" { if err := console.OpenAndDup(consolePath); err != nil { diff --git a/namespaces/sync_pipe.go b/namespaces/sync_pipe.go index 57851c5c6..ae6fefc30 100644 --- a/namespaces/sync_pipe.go +++ b/namespaces/sync_pipe.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "os" + "syscall" "github.com/docker/libcontainer/network" ) @@ -18,10 +19,14 @@ type SyncPipe struct { func NewSyncPipe() (s *SyncPipe, err error) { s = &SyncPipe{} - s.child, s.parent, err = os.Pipe() + + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) if err != nil { return nil, err } + s.child = os.NewFile(uintptr(fds[0]), "child syncpipe") + s.parent = os.NewFile(uintptr(fds[1]), "parent syncpipe") + return s, nil } @@ -51,6 +56,18 @@ func (s *SyncPipe) SendToChild(networkState *network.NetworkState) error { return err } s.parent.Write(data) + syscall.Shutdown(int(s.parent.Fd()), syscall.SHUT_WR) + return nil +} + +func (s *SyncPipe) BlockOnChild() error { + data, err := ioutil.ReadAll(s.parent) + if err != nil { + return nil + } + if len(data) > 0 { + return fmt.Errorf("Child error: %s", string(data)) + } return nil } @@ -69,6 +86,11 @@ func (s *SyncPipe) ReadFromParent() (*network.NetworkState, error) { } +func (s *SyncPipe) ReportError(err error) { + s.child.Write([]byte(err.Error())) + s.CloseChild() +} + func (s *SyncPipe) Close() error { if s.parent != nil { s.parent.Close() @@ -78,3 +100,10 @@ func (s *SyncPipe) Close() error { } return nil } + +func (s *SyncPipe) CloseChild() { + if s.child != nil { + s.child.Close() + s.child = nil + } +}