diff --git a/network/veth.go b/network/veth.go index 321c68ec..49e63f07 100644 --- a/network/veth.go +++ b/network/veth.go @@ -6,6 +6,9 @@ import ( "github.com/dotcloud/docker/pkg/libcontainer/utils" ) +// Veth is a network strategy that uses a bridge and creates +// a veth pair, one that stays outside on the host and the other +// is placed inside the container's namespace type Veth struct { } diff --git a/nsinit/command.go b/nsinit/command.go index b1c5631b..5eb378ac 100644 --- a/nsinit/command.go +++ b/nsinit/command.go @@ -8,8 +8,11 @@ import ( "syscall" ) +// CommandFactory takes the container's configuration and options passed by the +// parent processes and creates an *exec.Cmd that will be used to fork/exec the +// namespaced init process type CommandFactory interface { - Create(container *libcontainer.Container, console, logFile string, syncFd uintptr, args []string) *exec.Cmd + Create(container *libcontainer.Container, console string, syncFd uintptr, args []string) *exec.Cmd } type DefaultCommandFactory struct{} @@ -17,13 +20,12 @@ type DefaultCommandFactory struct{} // Create will return an exec.Cmd with the Cloneflags set to the proper namespaces // defined on the container's configuration and use the current binary as the init with the // args provided -func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console, logFile string, pipe uintptr, args []string) *exec.Cmd { +func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console string, pipe uintptr, args []string) *exec.Cmd { // get our binary name so we can always reexec ourself name := os.Args[0] command := exec.Command(name, append([]string{ "-console", console, "-pipe", fmt.Sprint(pipe), - "-log", logFile, "init"}, args...)...) command.SysProcAttr = &syscall.SysProcAttr{ diff --git a/nsinit/exec.go b/nsinit/exec.go index c4073235..b13326ba 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -28,31 +28,27 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [ } if container.Tty { - ns.logger.Printf("setting up master and console") - master, console, err = CreateMasterAndConsole() + master, console, err = system.CreateMasterAndConsole() if err != nil { return -1, err } term.SetMaster(master) } - command := ns.commandFactory.Create(container, console, ns.logFile, syncPipe.child.Fd(), args) + command := ns.commandFactory.Create(container, console, syncPipe.child.Fd(), args) if err := term.Attach(command); err != nil { return -1, err } defer term.Close() - ns.logger.Printf("staring init") if err := command.Start(); err != nil { return -1, err } - ns.logger.Printf("writing state file") if err := ns.stateWriter.WritePid(command.Process.Pid); err != nil { command.Process.Kill() return -1, err } defer func() { - ns.logger.Printf("removing state file") ns.stateWriter.DeletePid() }() @@ -68,24 +64,18 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [ } // Sync with child - ns.logger.Printf("closing sync pipes") syncPipe.Close() - ns.logger.Printf("waiting on process") if err := command.Wait(); err != nil { if _, ok := err.(*exec.ExitError); !ok { return -1, err } } - - exitCode := command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() - ns.logger.Printf("process ended with exit code %d", exitCode) - return exitCode, nil + return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil } func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) error { if container.Cgroups != nil { - ns.logger.Printf("setting up cgroups") if err := container.Cgroups.Apply(nspid); err != nil { return err } @@ -95,7 +85,6 @@ func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) er func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error { if container.Network != nil { - ns.logger.Printf("creating host network configuration type %s", container.Network.Type) strategy, err := network.GetStrategy(container.Network.Type) if err != nil { return err @@ -104,27 +93,9 @@ func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid if err != nil { return err } - ns.logger.Printf("sending %v as network context", networkContext) if err := pipe.SendToChild(networkContext); err != nil { return err } } return nil } - -// CreateMasterAndConsole will open /dev/ptmx on the host and retreive the -// pts name for use as the pty slave inside the container -func CreateMasterAndConsole() (*os.File, string, error) { - master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0) - if err != nil { - return nil, "", err - } - console, err := system.Ptsname(master) - if err != nil { - return nil, "", err - } - if err := system.Unlockpt(master); err != nil { - return nil, "", err - } - return master, console, nil -} diff --git a/nsinit/execin.go b/nsinit/execin.go index 9c33f69b..463196c7 100644 --- a/nsinit/execin.go +++ b/nsinit/execin.go @@ -18,7 +18,7 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s return -1, err } } - fds, err := getNsFds(nspid, container) + fds, err := ns.getNsFds(nspid, container) closeFds := func() { for _, f := range fds { system.Closefd(f) @@ -75,13 +75,13 @@ dropAndExec: if err := capabilities.DropCapabilities(container); err != nil { return -1, fmt.Errorf("drop capabilities %s", err) } - if err := system.Exec(args[0], args[0:], container.Env); err != nil { + if err := system.Execv(args[0], args[0:], container.Env); err != nil { return -1, err } panic("unreachable") } -func getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) { +func (ns *linuxNs) 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) diff --git a/nsinit/init.go b/nsinit/init.go index 5e33169b..1229560b 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -7,18 +7,17 @@ import ( "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/libcontainer/utils" "github.com/dotcloud/docker/pkg/system" "github.com/dotcloud/docker/pkg/user" "os" - "os/exec" - "path/filepath" "syscall" ) // 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 (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error { - rootfs, err := resolveRootfs(uncleanRootfs) + rootfs, err := utils.ResolveRootfs(uncleanRootfs) if err != nil { return err } @@ -34,7 +33,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol if console != "" { // close pipes so that we can replace it with the pty closeStdPipes() - slave, err := openTerminal(console, syscall.O_RDWR) + slave, err := system.OpenTerminal(console, syscall.O_RDWR) if err != nil { return fmt.Errorf("open terminal %s", err) } @@ -50,6 +49,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol return fmt.Errorf("setctty %s", err) } } + if err := system.ParentDeathSignal(); err != nil { return fmt.Errorf("parent deth signal %s", err) } @@ -73,18 +73,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol 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 - } - if err := system.Exec(name, args[0:], env); err != nil { - return fmt.Errorf("exec %s", err) - } - panic("unreachable") + return system.Execv(args[0], args[0:], container.Env) } func closeStdPipes() { @@ -93,18 +82,19 @@ func closeStdPipes() { 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) { - rootfs, err := filepath.Abs(uncleanRootfs) - if err != nil { - return "", err - } - return filepath.EvalSymlinks(rootfs) -} - func setupUser(container *libcontainer.Container) error { - if container.User != "" && container.User != "root" { + switch container.User { + case "root", "": + if err := system.Setgroups(nil); err != nil { + return err + } + if err := system.Setresgid(0, 0, 0); err != nil { + return err + } + if err := system.Setresuid(0, 0, 0); err != nil { + return err + } + default: uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid()) if err != nil { return err @@ -118,16 +108,6 @@ func setupUser(container *libcontainer.Container) error { if err := system.Setuid(uid); err != nil { return err } - } else { - if err := system.Setgroups(nil); err != nil { - return err - } - if err := system.Setresgid(0, 0, 0); err != nil { - return err - } - if err := system.Setresuid(0, 0, 0); err != nil { - return err - } } return nil } @@ -147,16 +127,6 @@ func dupSlave(slave *os.File) error { return nil } -// openTerminal is a clone of os.OpenFile without the O_CLOEXEC -// used to open the pty slave inside the container namespace -func openTerminal(name string, flag int) (*os.File, error) { - r, e := syscall.Open(name, flag, 0) - if e != nil { - return nil, &os.PathError{"open", name, e} - } - return os.NewFile(uintptr(r), name), nil -} - // setupVethNetwork uses the Network config if it is not nil to initialize // the new veth interface inside the container for use by changing the name to eth0 // setting the MTU and IP address along with the default gateway diff --git a/nsinit/nsinit.go b/nsinit/nsinit.go index 599461e4..f09a130a 100644 --- a/nsinit/nsinit.go +++ b/nsinit/nsinit.go @@ -2,9 +2,10 @@ package nsinit import ( "github.com/dotcloud/docker/pkg/libcontainer" - "log" ) +// NsInit is an interface with the public facing methods to provide high level +// exec operations on a container type NsInit interface { Exec(container *libcontainer.Container, term Terminal, args []string) (int, error) ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error) @@ -13,17 +14,13 @@ type NsInit interface { type linuxNs struct { root string - logFile string - logger *log.Logger commandFactory CommandFactory stateWriter StateWriter } -func NewNsInit(logger *log.Logger, logFile string, command CommandFactory, state StateWriter) NsInit { +func NewNsInit(command CommandFactory, state StateWriter) NsInit { return &linuxNs{ - logger: logger, commandFactory: command, stateWriter: state, - logFile: logFile, } } diff --git a/nsinit/nsinit/main.go b/nsinit/nsinit/main.go index c25037fa..e385e7fb 100644 --- a/nsinit/nsinit/main.go +++ b/nsinit/nsinit/main.go @@ -6,7 +6,6 @@ import ( "flag" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer/nsinit" - "io" "io/ioutil" "log" "os" @@ -16,7 +15,6 @@ import ( var ( console string pipeFd int - logFile string ) var ( @@ -26,7 +24,6 @@ var ( func registerFlags() { flag.StringVar(&console, "console", "", "console (pty slave) path") - flag.StringVar(&logFile, "log", "none", "log options (none, stderr, or a file path)") flag.IntVar(&pipeFd, "pipe", 0, "sync pipe fd") flag.Parse() @@ -113,26 +110,5 @@ func readPid() (int, error) { } func newNsInit() (nsinit.NsInit, error) { - logger, err := setupLogging() - if err != nil { - return nil, err - } - return nsinit.NewNsInit(logger, logFile, &nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{}), nil -} - -func setupLogging() (logger *log.Logger, err error) { - var writer io.Writer - - switch logFile { - case "stderr": - writer = os.Stderr - case "none", "": - writer = ioutil.Discard - default: - if writer, err = os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755); err != nil { - return - } - } - logger = log.New(writer, "", log.LstdFlags) - return + return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{}), nil } diff --git a/nsinit/state.go b/nsinit/state.go index 2dbaaa59..5c719e1c 100644 --- a/nsinit/state.go +++ b/nsinit/state.go @@ -7,6 +7,8 @@ import ( "path/filepath" ) +// StateWriter handles writing and deleting the pid file +// on disk type StateWriter interface { WritePid(pid int) error DeletePid() error diff --git a/utils/utils.go b/utils/utils.go index 5050997f..0d919bc4 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "encoding/hex" "io" + "path/filepath" ) // GenerateRandomName returns a new name joined with a prefix. This size @@ -15,3 +16,13 @@ func GenerateRandomName(prefix string, size int) (string, error) { } return prefix + hex.EncodeToString(id)[:size], nil } + +// 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) { + rootfs, err := filepath.Abs(uncleanRootfs) + if err != nil { + return "", err + } + return filepath.EvalSymlinks(rootfs) +}