diff --git a/configs/config.go b/configs/config.go index bf7daea0b..ca433b288 100644 --- a/configs/config.go +++ b/configs/config.go @@ -55,11 +55,6 @@ type Config struct { // WorkingDir will change the processes current working directory inside the container's rootfs WorkingDir string `json:"working_dir,omitempty"` - // Env will populate the processes environment with the provided values - // Any values from the parent processes will be cleared before the values - // provided in Env are provided to the process - Env []string `json:"environment,omitempty"` - // Console is the path to the console allocated to the container. Console string `json:"console,omitempty"` diff --git a/integration/exec_test.go b/integration/exec_test.go index 0f93957c7..6d68b4bab 100644 --- a/integration/exec_test.go +++ b/integration/exec_test.go @@ -261,6 +261,7 @@ func TestEnter(t *testing.T) { pconfig := libcontainer.Process{ Args: []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"}, + Env: standardEnvironment, Stdin: stdinR, Stdout: &stdout, } @@ -361,6 +362,7 @@ func TestFreeze(t *testing.T) { pconfig := libcontainer.Process{ Args: []string{"cat"}, + Env: standardEnvironment, Stdin: stdinR, } pid, err := container.Start(&pconfig) diff --git a/integration/template_test.go b/integration/template_test.go index 28160c986..1e7c418f0 100644 --- a/integration/template_test.go +++ b/integration/template_test.go @@ -6,6 +6,13 @@ import ( "github.com/docker/libcontainer/configs" ) +var standardEnvironment = []string{ + "HOME=/root", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOSTNAME=integration", + "TERM=xterm", +} + // newTemplateConfig returns a base template for running a container // // it uses a network strategy of just setting a loopback interface @@ -43,14 +50,8 @@ func newTemplateConfig(rootfs string) *configs.Config { AllowedDevices: configs.DefaultAllowedDevices, }, - Devices: configs.DefaultAutoCreatedDevices, - Hostname: "integration", - Env: []string{ - "HOME=/root", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=integration", - "TERM=xterm", - }, + Devices: configs.DefaultAutoCreatedDevices, + Hostname: "integration", Networks: []*configs.Network{ { Type: "loopback", diff --git a/integration/utils_test.go b/integration/utils_test.go index 75cba52b1..e3fdb779a 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -93,6 +93,7 @@ func runContainer(config *configs.Config, console string, args ...string) (buffe process := &libcontainer.Process{ Args: args, + Env: standardEnvironment, Stdin: buffers.Stdin, Stdout: buffers.Stdout, Stderr: buffers.Stderr, diff --git a/linux_container.go b/linux_container.go index d30b5ba5b..7e73b0573 100644 --- a/linux_container.go +++ b/linux_container.go @@ -18,10 +18,6 @@ import ( "github.com/golang/glog" ) -const ( - EXIT_SIGNAL_OFFSET = 128 -) - type pid struct { Pid int `json:"Pid"` } @@ -50,8 +46,7 @@ func (c *linuxContainer) Status() (configs.Status, error) { return configs.Destroyed, nil } // return Running if the init process is alive - err := syscall.Kill(c.state.InitPid, 0) - if err != nil { + if err := syscall.Kill(c.state.InitPid, 0); err != nil { if err == syscall.ESRCH { return configs.Destroyed, nil } @@ -96,10 +91,9 @@ func (c *linuxContainer) Start(process *Process) (int, error) { cmd := c.commandTemplate(process) if status != configs.Destroyed { // TODO: (crosbymichael) check out console use for execin - return c.startNewProcess(cmd, process.Args) - //return namespaces.ExecIn(process.Args, c.config.Env, "", cmd, c.config, c.state) + return c.startNewProcess(cmd, process) } - if err := c.startInitialProcess(cmd, process.Args); err != nil { + if err := c.startInitialProcess(cmd, process); err != nil { return -1, err } return c.state.InitPid, nil @@ -112,7 +106,7 @@ func (c *linuxContainer) commandTemplate(process *Process) *exec.Cmd { cmd.Stdin = process.Stdin cmd.Stdout = process.Stdout cmd.Stderr = process.Stderr - cmd.Env = c.config.Env + cmd.Env = process.Env cmd.Dir = c.config.Rootfs if cmd.SysProcAttr == nil { cmd.SysProcAttr = &syscall.SysProcAttr{} @@ -122,9 +116,9 @@ func (c *linuxContainer) commandTemplate(process *Process) *exec.Cmd { } // startNewProcess adds another process to an already running container -func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, args []string) (int, error) { +func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, process *Process) (int, error) { glog.Info("start new container process") - parent, child, err := newInitPipe() + parent, child, err := c.newInitPipe() if err != nil { return -1, err } @@ -169,18 +163,20 @@ func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, args []string) (int, err } if err := json.NewEncoder(parent).Encode(&initConfig{ Config: c.config, - Args: args, + Args: process.Args, + Env: process.Env, }); err != nil { return terminate(err) } return pid.Pid, nil } -func (c *linuxContainer) startInitialProcess(cmd *exec.Cmd, args []string) error { +// startInitialProcess starts PID 1 for the container. +func (c *linuxContainer) startInitialProcess(cmd *exec.Cmd, process *Process) error { glog.Info("starting container initial process") // 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() + parent, child, err := c.newInitPipe() if err != nil { return err } @@ -239,13 +235,14 @@ func (c *linuxContainer) startInitialProcess(cmd *exec.Cmd, args []string) error return terminate(err) } iconfig := &initConfig{ - Args: args, + Args: process.Args, Config: c.config, + Env: process.Env, NetworkState: &networkState, } // Start the setup process to setup the init process if c.config.Namespaces.Contains(configs.NEWUSER) { - if err = executeSetupCmd(cmd.Args, cmd.Process.Pid, c.config, iconfig, &networkState); err != nil { + if err = c.executeSetupCmd(cmd.Args, cmd.Process.Pid, c.config, iconfig, &networkState); err != nil { return terminate(err) } } @@ -281,6 +278,7 @@ func (c *linuxContainer) Destroy() error { if status != configs.Destroyed { return newGenericError(nil, ContainerNotStopped) } + // TODO: remove cgroups return os.RemoveAll(c.root) } @@ -297,6 +295,7 @@ func (c *linuxContainer) Signal(signal os.Signal) error { panic("not implemented") } +// TODO: rename to be more descriptive func (c *linuxContainer) OOM() (<-chan struct{}, error) { return NotifyOnOOM(c.state) } @@ -322,7 +321,7 @@ func (c *linuxContainer) updateStateFile() error { } // New returns a newly initialized Pipe for communication between processes -func newInitPipe() (parent *os.File, child *os.File, err error) { +func (c *linuxContainer) newInitPipe() (parent *os.File, child *os.File, err error) { fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) if err != nil { return nil, nil, err @@ -392,9 +391,9 @@ func (c *linuxContainer) initializeNetworking(nspid int, networkState *configs.N return nil } -func executeSetupCmd(args []string, ppid int, container *configs.Config, process *initConfig, networkState *configs.NetworkState) error { +func (c *linuxContainer) executeSetupCmd(args []string, ppid int, container *configs.Config, process *initConfig, networkState *configs.NetworkState) error { command := exec.Command(args[0], args[1:]...) - parent, child, err := newInitPipe() + parent, child, err := c.newInitPipe() if err != nil { return err } diff --git a/linux_init.go b/linux_init.go index b35257d0d..daed34e92 100644 --- a/linux_init.go +++ b/linux_init.go @@ -30,6 +30,7 @@ const ( // Process is used for transferring parameters from Exec() to Init() type initConfig struct { Args []string `json:"args,omitempty"` + Env []string `json:"env,omitempty"` Config *configs.Config `json:"config,omitempty"` NetworkState *configs.NetworkState `json:"network_state,omitempty"` } @@ -43,18 +44,20 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) { if err := json.NewDecoder(pipe).Decode(&config); err != nil { return nil, err } - if err := populateProcessEnvironment(config.Config.Env); err != nil { + if err := populateProcessEnvironment(config.Env); err != nil { return nil, err } switch t { case initSetns: return &linuxSetnsInit{ args: config.Args, + env: config.Env, config: config.Config, }, nil case initUserns: return &linuxUsernsInit{ args: config.Args, + env: config.Env, config: config.Config, }, nil case initUsernsSideCar: @@ -65,6 +68,7 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) { case initStandard: return &linuxStandardInit{ config: config, + env: config.Env, }, nil } return nil, fmt.Errorf("unknown init type %q", t) diff --git a/linux_setns_init.go b/linux_setns_init.go index 356e41b09..a9a1d27cd 100644 --- a/linux_setns_init.go +++ b/linux_setns_init.go @@ -13,6 +13,7 @@ import ( // inside an existing container. type linuxSetnsInit struct { args []string + env []string config *configs.Config } @@ -31,5 +32,5 @@ func (l *linuxSetnsInit) Init() error { return err } } - return system.Execv(l.args[0], l.args[0:], l.config.Env) + return system.Execv(l.args[0], l.args[0:], l.env) } diff --git a/linux_standard_init.go b/linux_standard_init.go index 6c0a4661f..b497ec80d 100644 --- a/linux_standard_init.go +++ b/linux_standard_init.go @@ -16,6 +16,7 @@ import ( type linuxStandardInit struct { config *initConfig + env []string } func (l *linuxStandardInit) Init() error { @@ -86,5 +87,5 @@ func (l *linuxStandardInit) Init() error { if syscall.Getppid() == 1 { return syscall.Kill(syscall.Getpid(), syscall.SIGKILL) } - return system.Execv(l.config.Args[0], l.config.Args[0:], l.config.Config.Env) + return system.Execv(l.config.Args[0], l.config.Args[0:], l.env) } diff --git a/linux_userns_init.go b/linux_userns_init.go index cee2ebb5f..b44c191e2 100644 --- a/linux_userns_init.go +++ b/linux_userns_init.go @@ -15,6 +15,7 @@ import ( type linuxUsernsInit struct { args []string + env []string config *configs.Config } @@ -76,5 +77,5 @@ func (l *linuxUsernsInit) Init() error { if syscall.Getppid() == 1 { return syscall.Kill(syscall.Getpid(), syscall.SIGKILL) } - return system.Execv(l.args[0], l.args[0:], l.config.Env) + return system.Execv(l.args[0], l.args[0:], l.env) } diff --git a/process.go b/process.go index c26b8bf59..e80dda0c8 100644 --- a/process.go +++ b/process.go @@ -1,6 +1,9 @@ package libcontainer -import "io" +import ( + "io" + "os/exec" +) // Process specifies the configuration and IO for a process inside // a container. @@ -8,6 +11,9 @@ type Process struct { // The command to be run followed by any arguments. Args []string + // Env specifies the environment variables for the process. + Env []string + // Stdin is a pointer to a reader which provides the standard input stream. Stdin io.Reader @@ -16,4 +22,6 @@ type Process struct { // Stderr is a pointer to a writer which receives the standard error stream. Stderr io.Writer + + cmd *exec.Cmd } diff --git a/utils/utils.go b/utils/utils.go index 76184ce00..094bce530 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -10,6 +10,10 @@ import ( "syscall" ) +const ( + exitSignalOffset = 128 +) + // GenerateRandomName returns a new name joined with a prefix. This size // specified is used to truncate the randomly generated value func GenerateRandomName(prefix string, size int) (string, error) { @@ -53,3 +57,12 @@ func CloseExecFrom(minFd int) error { } return nil } + +// ExitStatus returns the correct exit status for a process based on if it +// was signaled or existed cleanly. +func ExitStatus(status syscall.WaitStatus) int { + if status.Signaled() { + return exitSignalOffset + int(status.Signal()) + } + return status.ExitStatus() +}