linux_container: fork an init process in a new set of namespaces
Use namespace.Exec() and namespace.Init() to execute processes in CT. Now an init process is actually executed in a new container. This series doesn't change code about creating containers, it only reworks code according with new API. Signed-off-by: Andrey Vagin <avagin@openvz.org>
This commit is contained in:
parent
c406a6b6e0
commit
5ecd29c1f2
|
@ -11,6 +11,7 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/docker/libcontainer/configs"
|
||||
"github.com/docker/libcontainer/namespaces"
|
||||
"github.com/docker/libcontainer/network"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
@ -119,16 +120,17 @@ func (c *linuxContainer) startInitProcess(config *ProcessConfig) error {
|
|||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
|
||||
cmd.SysProcAttr.Cloneflags = uintptr(namespaces.GetNamespaceFlags(c.config.Namespaces))
|
||||
cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
|
||||
|
||||
//FIXME call namespaces.Exec()
|
||||
if err := cmd.Start(); err != nil {
|
||||
err := namespaces.Exec(config.Args, config.Env, cmd, c.config, c.state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.state.InitPid = cmd.Process.Pid
|
||||
err := c.updateStateFile()
|
||||
err = c.updateStateFile()
|
||||
if err != nil {
|
||||
// FIXME c.Kill()
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
|
||||
"github.com/docker/libcontainer/configs"
|
||||
"github.com/docker/libcontainer/namespaces"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -164,7 +165,7 @@ func (l *linuxFactory) loadContainerState(root string) (*configs.State, error) {
|
|||
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
|
||||
// This is a low level implementation detail of the reexec and should not be consumed externally
|
||||
func (f *linuxFactory) StartInitialization(pipefd uintptr) (err error) {
|
||||
pipe := os.NewFile(uintptr(pipefd), "pipe")
|
||||
|
||||
/* FIXME call namespaces.Init() */
|
||||
return nil
|
||||
return namespaces.Init(pipe)
|
||||
}
|
||||
|
|
|
@ -21,36 +21,45 @@ import (
|
|||
// Move this to libcontainer package.
|
||||
// Exec performs setup outside of a namespace so that a container can be
|
||||
// executed. Exec is a high level function for working with container namespaces.
|
||||
func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer, console, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
|
||||
func Exec(args []string, env []string, command *exec.Cmd, container *configs.Config, state *configs.State) error {
|
||||
var err error
|
||||
|
||||
// create a pipe so that we can syncronize with the namespaced process and
|
||||
// pass the state and configuration to the child process
|
||||
parent, child, err := newInitPipe()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
return err
|
||||
}
|
||||
defer parent.Close()
|
||||
|
||||
command := createCommand(container, console, dataPath, os.Args[0], child, args)
|
||||
// Note: these are only used in non-tty mode
|
||||
// if there is a tty for the container it will be opened within the namespace and the
|
||||
// fds will be duped to stdin, stdiout, and stderr
|
||||
command.Stdin = stdin
|
||||
command.Stdout = stdout
|
||||
command.Stderr = stderr
|
||||
command.ExtraFiles = []*os.File{child}
|
||||
command.Dir = container.RootFs
|
||||
|
||||
if err := command.Start(); err != nil {
|
||||
child.Close()
|
||||
return -1, err
|
||||
return err
|
||||
}
|
||||
child.Close()
|
||||
|
||||
terminate := func(terr error) (int, error) {
|
||||
terminate := func(terr error) error {
|
||||
// TODO: log the errors for kill and wait
|
||||
command.Process.Kill()
|
||||
command.Wait()
|
||||
return -1, terr
|
||||
return terr
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(parent)
|
||||
|
||||
if err := encoder.Encode(container); err != nil {
|
||||
return terminate(err)
|
||||
}
|
||||
|
||||
process := processArgs{
|
||||
Env: env,
|
||||
Args: args,
|
||||
}
|
||||
if err := encoder.Encode(process); err != nil {
|
||||
return terminate(err)
|
||||
}
|
||||
|
||||
started, err := system.GetProcessStartTime(command.Process.Pid)
|
||||
|
@ -71,7 +80,7 @@ func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer,
|
|||
return terminate(err)
|
||||
}
|
||||
// send the state to the container's init process then shutdown writes for the parent
|
||||
if err := json.NewEncoder(parent).Encode(networkState); err != nil {
|
||||
if err := encoder.Encode(networkState); err != nil {
|
||||
return terminate(err)
|
||||
}
|
||||
// shutdown writes for the parent side of the pipe
|
||||
|
@ -79,18 +88,6 @@ func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer,
|
|||
return terminate(err)
|
||||
}
|
||||
|
||||
state := &configs.State{
|
||||
InitPid: command.Process.Pid,
|
||||
InitStartTime: started,
|
||||
NetworkState: networkState,
|
||||
CgroupPaths: cgroupPaths,
|
||||
}
|
||||
|
||||
if err := configs.SaveState(dataPath, state); err != nil {
|
||||
return terminate(err)
|
||||
}
|
||||
defer configs.DeleteState(dataPath)
|
||||
|
||||
// wait for the child process to fully complete and receive an error message
|
||||
// if one was encoutered
|
||||
var ierr *initError
|
||||
|
@ -101,16 +98,12 @@ func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer,
|
|||
return terminate(ierr)
|
||||
}
|
||||
|
||||
if startCallback != nil {
|
||||
startCallback()
|
||||
}
|
||||
state.InitPid = command.Process.Pid
|
||||
state.InitStartTime = started
|
||||
state.NetworkState = networkState
|
||||
state.CgroupPaths = cgroupPaths
|
||||
|
||||
if err := command.Wait(); err != nil {
|
||||
if _, ok := err.(*exec.ExitError); !ok {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultCreateCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
||||
|
|
|
@ -24,13 +24,20 @@ import (
|
|||
"github.com/docker/libcontainer/utils"
|
||||
)
|
||||
|
||||
// Process is used for transferring parameters from Exec() to Init()
|
||||
type processArgs struct {
|
||||
Args []string `json:"args,omitempty"`
|
||||
Env []string `json:"environment,omitempty"`
|
||||
ConsolePath string `json:"console_path,omitempty"`
|
||||
}
|
||||
|
||||
// TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work.
|
||||
// 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.
|
||||
// The caller of Init function has to ensure that the go runtime is locked to an OS thread
|
||||
// (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended.
|
||||
func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os.File, args []string) (err error) {
|
||||
func Init(pipe *os.File) (err error) {
|
||||
defer func() {
|
||||
// if we have an error during the initialization of the container's init then send it back to the
|
||||
// parent process in the form of an initError.
|
||||
|
@ -48,6 +55,23 @@ func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os
|
|||
pipe.Close()
|
||||
}()
|
||||
|
||||
decoder := json.NewDecoder(pipe)
|
||||
|
||||
var container *configs.Config
|
||||
if err := decoder.Decode(&container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var process *processArgs
|
||||
if err := decoder.Decode(&process); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uncleanRootfs, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -61,22 +85,22 @@ func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os
|
|||
|
||||
// We always read this as it is a way to sync with the parent as well
|
||||
var networkState *network.NetworkState
|
||||
if err := json.NewDecoder(pipe).Decode(&networkState); err != nil {
|
||||
if err := decoder.Decode(&networkState); err != nil {
|
||||
return err
|
||||
}
|
||||
// join any namespaces via a path to the namespace fd if provided
|
||||
if err := joinExistingNamespaces(container.Namespaces); err != nil {
|
||||
return err
|
||||
}
|
||||
if consolePath != "" {
|
||||
if err := console.OpenAndDup(consolePath); err != nil {
|
||||
if process.ConsolePath != "" {
|
||||
if err := console.OpenAndDup(process.ConsolePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := syscall.Setsid(); err != nil {
|
||||
return fmt.Errorf("setsid %s", err)
|
||||
}
|
||||
if consolePath != "" {
|
||||
if process.ConsolePath != "" {
|
||||
if err := system.Setctty(); err != nil {
|
||||
return fmt.Errorf("setctty %s", err)
|
||||
}
|
||||
|
@ -96,7 +120,7 @@ func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os
|
|||
label.Init()
|
||||
|
||||
if err := mount.InitializeMountNamespace(rootfs,
|
||||
consolePath,
|
||||
process.ConsolePath,
|
||||
container.RestrictSys,
|
||||
(*mount.MountConfig)(container.MountConfig)); err != nil {
|
||||
return fmt.Errorf("setup mount namespace %s", err)
|
||||
|
@ -138,7 +162,7 @@ func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os
|
|||
return fmt.Errorf("restore parent death signal %s", err)
|
||||
}
|
||||
|
||||
return system.Execv(args[0], args[0:], os.Environ())
|
||||
return system.Execv(process.Args[0], process.Args[0:], process.Env)
|
||||
}
|
||||
|
||||
// RestoreParentDeathSignal sets the parent death signal to old.
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/docker/libcontainer/system"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
|
@ -36,9 +34,5 @@ func initAction(context *cli.Context) {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
args := []string(context.Args())
|
||||
|
||||
if err := system.Execv(args[0], args[0:], os.Environ()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
panic("This line should never been executed")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue