diff --git a/pkg/libcontainer/nsinit/exec.go b/pkg/libcontainer/nsinit/exec.go index 3cbe43ae7a..ec75e9c923 100644 --- a/pkg/libcontainer/nsinit/exec.go +++ b/pkg/libcontainer/nsinit/exec.go @@ -3,7 +3,6 @@ package nsinit import ( - "encoding/json" "fmt" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer/network" @@ -28,11 +27,10 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io. // create a pipe so that we can syncronize with the namespaced process and // pass the veth name to the child - r, w, err := os.Pipe() + syncPipe, err := NewSyncPipe() if err != nil { return -1, err } - system.UsetCloseOnExec(r.Fd()) if container.Tty { log.Printf("setting up master and console") @@ -42,8 +40,7 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io. } } - command := CreateCommand(container, console, logFile, r.Fd(), args) - + command := CreateCommand(container, console, logFile, syncPipe.child.Fd(), args) if container.Tty { log.Printf("starting copy for tty") go io.Copy(stdout, master) @@ -79,15 +76,14 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io. command.Process.Kill() return -1, err } - if err := InitializeNetworking(container, command.Process.Pid, w); err != nil { + if err := InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil { command.Process.Kill() return -1, err } // Sync with child log.Printf("closing sync pipes") - w.Close() - r.Close() + syncPipe.Close() log.Printf("waiting on process") if err := command.Wait(); err != nil { @@ -109,7 +105,7 @@ func SetupCgroups(container *libcontainer.Container, nspid int) error { return nil } -func InitializeNetworking(container *libcontainer.Container, nspid int, pipe io.Writer) error { +func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error { if container.Network != nil { log.Printf("creating host network configuration type %s", container.Network.Type) strategy, err := network.GetStrategy(container.Network.Type) @@ -121,24 +117,13 @@ func InitializeNetworking(container *libcontainer.Container, nspid int, pipe io. return err } log.Printf("sending %v as network context", networkContext) - if err := SendContext(pipe, networkContext); err != nil { + if err := pipe.SendToChild(networkContext); err != nil { return err } } return nil } -// SendContext writes the veth pair name to the child's stdin then closes the -// pipe so that the child stops waiting for more data -func SendContext(pipe io.Writer, context libcontainer.Context) error { - data, err := json.Marshal(context) - if err != nil { - return err - } - pipe.Write(data) - return nil -} - // SetupWindow gets the parent window size and sets the master // pty to the current size and set the parents mode to RAW func SetupWindow(master, parent *os.File) (*term.State, error) { diff --git a/pkg/libcontainer/nsinit/init.go b/pkg/libcontainer/nsinit/init.go index f530d4a52a..cdedc14769 100644 --- a/pkg/libcontainer/nsinit/init.go +++ b/pkg/libcontainer/nsinit/init.go @@ -3,14 +3,11 @@ package nsinit import ( - "encoding/json" "fmt" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer/capabilities" "github.com/dotcloud/docker/pkg/libcontainer/network" "github.com/dotcloud/docker/pkg/system" - "io" - "io/ioutil" "log" "os" "os/exec" @@ -20,7 +17,7 @@ import ( // 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.Container, uncleanRootfs, console string, pipe io.ReadCloser, args []string) error { +func Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error { rootfs, err := resolveRootfs(uncleanRootfs) if err != nil { return err @@ -28,16 +25,18 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe log.Printf("initializing namespace at %s", rootfs) // We always read this as it is a way to sync with the parent as well - context, err := GetContextFromParent(pipe) + context, err := syncPipe.ReadFromParent() if err != nil { + syncPipe.Close() return err } + syncPipe.Close() + log.Printf("received context from parent %v", context) + if console != "" { log.Printf("setting up console for %s", console) // close pipes so that we can replace it with the pty - os.Stdin.Close() - os.Stdout.Close() - os.Stderr.Close() + closeStdPipes() slave, err := openTerminal(console, syscall.O_RDWR) if err != nil { return fmt.Errorf("open terminal %s", err) @@ -79,18 +78,27 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe return fmt.Errorf("chdir to %s %s", container.WorkingDir, err) } } + return execArgs(args, container.Env) +} + +func execArgs(args []string, env []string) error { name, err := exec.LookPath(args[0]) if err != nil { return err } - log.Printf("execing %s goodbye", name) - if err := system.Exec(name, args[0:], container.Env); err != nil { + if err := system.Exec(name, args[0:], env); err != nil { return fmt.Errorf("exec %s", err) } panic("unreachable") } +func closeStdPipes() { + os.Stdin.Close() + os.Stdout.Close() + os.Stderr.Close() +} + // resolveRootfs ensures that the current working directory is // not a symlink and returns the absolute path to the rootfs func resolveRootfs(uncleanRootfs string) (string, error) { @@ -153,19 +161,3 @@ func setupNetwork(config *libcontainer.Network, context libcontainer.Context) er } return nil } - -func GetContextFromParent(pipe io.ReadCloser) (libcontainer.Context, error) { - defer pipe.Close() - data, err := ioutil.ReadAll(pipe) - if err != nil { - return nil, fmt.Errorf("error reading from stdin %s", err) - } - var context libcontainer.Context - if len(data) > 0 { - if err := json.Unmarshal(data, &context); err != nil { - return nil, err - } - log.Printf("received context %v", context) - } - return context, nil -} diff --git a/pkg/libcontainer/nsinit/nsinit/main.go b/pkg/libcontainer/nsinit/nsinit/main.go index 28d42d4643..2400ab6903 100644 --- a/pkg/libcontainer/nsinit/nsinit/main.go +++ b/pkg/libcontainer/nsinit/nsinit/main.go @@ -74,7 +74,11 @@ func main() { if flag.NArg() < 2 { log.Fatal(ErrWrongArguments) } - if err := nsinit.Init(container, cwd, console, os.NewFile(uintptr(pipeFd), "pipe"), flag.Args()[1:]); err != nil { + syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(pipeFd)) + if err != nil { + log.Fatal(err) + } + if err := nsinit.Init(container, cwd, console, syncPipe, flag.Args()[1:]); err != nil { log.Fatal(err) } default: diff --git a/pkg/libcontainer/nsinit/sync_pipe.go b/pkg/libcontainer/nsinit/sync_pipe.go new file mode 100644 index 0000000000..7b29e98680 --- /dev/null +++ b/pkg/libcontainer/nsinit/sync_pipe.go @@ -0,0 +1,73 @@ +package nsinit + +import ( + "encoding/json" + "fmt" + "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/pkg/system" + "io/ioutil" + "os" +) + +// SyncPipe allows communication to and from the child processes +// to it's parent and allows the two independent processes to +// syncronize their state. +type SyncPipe struct { + parent, child *os.File +} + +func NewSyncPipe() (s *SyncPipe, err error) { + s = &SyncPipe{} + s.child, s.parent, err = os.Pipe() + if err != nil { + return nil, err + } + system.UsetCloseOnExec(s.child.Fd()) + return s, nil +} + +func NewSyncPipeFromFd(parendFd, childFd uintptr) (*SyncPipe, error) { + s := &SyncPipe{} + if parendFd > 0 { + s.parent = os.NewFile(parendFd, "parendPipe") + } else if childFd > 0 { + s.child = os.NewFile(childFd, "childPipe") + } else { + return nil, fmt.Errorf("no valid sync pipe fd specified") + } + return s, nil +} + +func (s *SyncPipe) SendToChild(context libcontainer.Context) error { + data, err := json.Marshal(context) + if err != nil { + return err + } + s.parent.Write(data) + return nil +} + +func (s *SyncPipe) ReadFromParent() (libcontainer.Context, error) { + data, err := ioutil.ReadAll(s.child) + if err != nil { + return nil, fmt.Errorf("error reading from sync pipe %s", err) + } + var context libcontainer.Context + if len(data) > 0 { + if err := json.Unmarshal(data, &context); err != nil { + return nil, err + } + } + return context, nil + +} + +func (s *SyncPipe) Close() error { + if s.parent != nil { + s.parent.Close() + } + if s.child != nil { + s.child.Close() + } + return nil +}