func execAction(context *cli.Context) { var nspid, exitCode int container, err := loadContainer() if err != nil { log.Fatal(err) } if nspid, err = readPid(); err != nil && !os.IsNotExist(err) { log.Fatalf("unable to read pid: %s", err) } if nspid > 0 { err = namespaces.ExecIn(container, nspid, []string(context.Args())) } else { term := namespaces.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty) exitCode, err = startContainer(container, term, dataPath, []string(context.Args())) } if err != nil { log.Fatalf("failed to exec: %s", err) } os.Exit(exitCode) }
func execAction(context *cli.Context) { var exitCode int container, err := loadContainer() if err != nil { log.Fatal(err) } state, err := libcontainer.GetState(dataPath) if err != nil && !os.IsNotExist(err) { log.Fatalf("unable to read state.json: %s", err) } if state != nil { err = namespaces.ExecIn(container, state, []string(context.Args())) } else { term := namespaces.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty) exitCode, err = startContainer(container, term, dataPath, []string(context.Args())) } if err != nil { log.Fatalf("failed to exec: %s", err) } os.Exit(exitCode) }
// TODO(vishh): Add support for running in priviledged mode and running as a different user. func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { active := d.activeContainers[c.ID] if active == nil { return -1, fmt.Errorf("No active container exists with ID %s", c.ID) } state, err := libcontainer.GetState(filepath.Join(d.root, c.ID)) if err != nil { return -1, fmt.Errorf("State unavailable for container with ID %s. The container may have been cleaned up already. Error: %s", c.ID, err) } var term execdriver.Terminal if processConfig.Tty { term, err = NewTtyConsole(processConfig, pipes) } else { term, err = execdriver.NewStdConsole(processConfig, pipes) } processConfig.Terminal = term args := append([]string{processConfig.Entrypoint}, processConfig.Arguments...) return namespaces.ExecIn(active.container, state, args, os.Args[0], "exec", processConfig.Stdin, processConfig.Stdout, processConfig.Stderr, processConfig.Console, func(cmd *exec.Cmd) { if startCallback != nil { startCallback(&c.ProcessConfig, cmd.Process.Pid) } }) }
// the process for execing a new process inside an existing container is that we have to exec ourself // with the nsenter argument so that the C code can setns an the namespaces that we require. Then that // code path will drop us into the path that we can do the final setup of the namespace and exec the users // application. func startInExistingContainer(config *libcontainer.Config, state *libcontainer.State, action string, context *cli.Context) (int, error) { var ( master *os.File console string err error sigc = make(chan os.Signal, 10) stdin = os.Stdin stdout = os.Stdout stderr = os.Stderr ) signal.Notify(sigc) if config.Tty { stdin = nil stdout = nil stderr = nil master, console, err = consolepkg.CreateMasterAndConsole() if err != nil { return -1, err } go io.Copy(master, os.Stdin) go io.Copy(os.Stdout, master) state, err := term.SetRawTerminal(os.Stdin.Fd()) if err != nil { return -1, err } defer term.RestoreTerminal(os.Stdin.Fd(), state) } startCallback := func(cmd *exec.Cmd) { go func() { resizeTty(master) for sig := range sigc { switch sig { case syscall.SIGWINCH: resizeTty(master) default: cmd.Process.Signal(sig) } } }() } return namespaces.ExecIn(config, state, context.Args(), os.Args[0], action, stdin, stdout, stderr, console, startCallback) }
func TestExecInRlimit(t *testing.T) { if testing.Short() { return } rootfs, err := newRootFs() if err != nil { t.Fatal(err) } defer remove(rootfs) config := newTemplateConfig(rootfs) if err := writeConfig(config); err != nil { t.Fatalf("failed to write config %s", err) } containerCmd, statePath, containerErr := startLongRunningContainer(config) defer func() { // kill the container if containerCmd.Process != nil { containerCmd.Process.Kill() } if err := <-containerErr; err != nil { t.Fatal(err) } }() // start the exec process state, err := libcontainer.GetState(statePath) if err != nil { t.Fatalf("failed to get state %s", err) } buffers := newStdBuffers() execErr := make(chan error, 1) go func() { _, err := namespaces.ExecIn(config, state, []string{"/bin/sh", "-c", "ulimit -n"}, os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr, "", nil) execErr <- err }() if err := <-execErr; err != nil { t.Fatalf("exec finished with error %s", err) } out := buffers.Stdout.String() if limit := strings.TrimSpace(out); limit != "1024" { t.Fatalf("expected rlimit to be 1024, got %s", limit) } }
func TestExecIn(t *testing.T) { if testing.Short() { return } rootfs, err := newRootFs() if err != nil { t.Fatal(err) } defer remove(rootfs) config := newTemplateConfig(rootfs) if err := writeConfig(config); err != nil { t.Fatalf("failed to write config %s", err) } containerCmd, statePath, containerErr := startLongRunningContainer(config) defer func() { // kill the container if containerCmd.Process != nil { containerCmd.Process.Kill() } if err := <-containerErr; err != nil { t.Fatal(err) } }() // start the exec process state, err := libcontainer.GetState(statePath) if err != nil { t.Fatalf("failed to get state %s", err) } buffers := newStdBuffers() execErr := make(chan error, 1) go func() { _, err := namespaces.ExecIn(config, state, []string{"ps"}, os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr, "", nil) execErr <- err }() if err := <-execErr; err != nil { t.Fatalf("exec finished with error %s", err) } out := buffers.Stdout.String() if !strings.Contains(out, "sleep 10") || !strings.Contains(out, "ps") { t.Fatalf("unexpected running process, output %q", out) } }