// ExecIn reexec's the initPath with the argv 0 rewrite to "nsenter" so that it is able to run the
// setns code in a single threaded environment joining the existing containers' namespaces.
func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath string,
	stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) {

	args := []string{"nsenter", "--nspid", strconv.Itoa(state.InitPid)}

	if console != "" {
		args = append(args, "--console", console)
	}

	cmd := &exec.Cmd{
		Path: initPath,
		Args: append(args, append([]string{"--"}, userArgs...)...),
	}

	if filepath.Base(initPath) == initPath {
		if lp, err := exec.LookPath(initPath); err == nil {
			cmd.Path = lp
		}
	}

	pipe, err := syncpipe.NewSyncPipe()
	if err != nil {
		return -1, err
	}
	defer pipe.Close()

	// 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
	cmd.Stdin = stdin
	cmd.Stdout = stdout
	cmd.Stderr = stderr

	cmd.ExtraFiles = []*os.File{pipe.Child()}

	if err := cmd.Start(); err != nil {
		return -1, err
	}
	pipe.CloseChild()

	if err := pipe.SendToChild(container); err != nil {
		cmd.Process.Kill()
		cmd.Wait()
		return -1, err
	}

	if startCallback != nil {
		startCallback(cmd)
	}

	if err := cmd.Wait(); err != nil {
		if _, ok := err.(*exec.ExitError); !ok {
			return -1, err
		}
	}

	return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
}
Beispiel #2
0
// TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work.
// 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 *libcontainer.Config, term Terminal, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
	var (
		master  *os.File
		console string
		err     error
	)

	// create a pipe so that we can syncronize with the namespaced process and
	// pass the veth name to the child
	syncPipe, err := syncpipe.NewSyncPipe()
	if err != nil {
		return -1, err
	}
	defer syncPipe.Close()

	if container.Tty {
		master, console, err = system.CreateMasterAndConsole()
		if err != nil {
			return -1, err
		}
		term.SetMaster(master)
	}

	command := createCommand(container, console, rootfs, dataPath, os.Args[0], syncPipe.Child(), args)

	if err := term.Attach(command); err != nil {
		return -1, err
	}
	defer term.Close()

	if err := command.Start(); err != nil {
		return -1, err
	}

	// Now we passed the pipe to the child, close our side
	syncPipe.CloseChild()

	started, err := system.GetProcessStartTime(command.Process.Pid)
	if err != nil {
		return -1, err
	}

	// Do this before syncing with child so that no children
	// can escape the cgroup
	cleaner, err := SetupCgroups(container, command.Process.Pid)
	if err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}
	if cleaner != nil {
		defer cleaner.Cleanup()
	}

	var networkState network.NetworkState
	if err := InitializeNetworking(container, command.Process.Pid, syncPipe, &networkState); err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}

	state := &libcontainer.State{
		InitPid:       command.Process.Pid,
		InitStartTime: started,
		NetworkState:  networkState,
	}

	if err := libcontainer.SaveState(dataPath, state); err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}
	defer libcontainer.DeleteState(dataPath)

	// Sync with child
	if err := syncPipe.ReadFromChild(); err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}

	if startCallback != nil {
		startCallback()
	}

	if err := command.Wait(); err != nil {
		if _, ok := err.(*exec.ExitError); !ok {
			return -1, err
		}
	}
	return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
}
Beispiel #3
0
func (m *MonitorProxy) Start() error {
	var (
		exitStatus int
		afterRun   bool
	)

	if !m.hasCmd {
		return fmt.Errorf("MonitorProxy haven't create cmd")
	}

	defer func() {
		if afterRun {
			// StatePoller get container state from watch API
			//m.container.SetStopped(exitStatus)

			// clean container resources
			m.Stop()

			m.container.monitorState.SetStopped(exitStatus)
			log.Debugf("Monitor process exit with code %v", exitStatus)
			if err := m.container.WriteMonitorState(); err != nil {
				log.Errorf("write monitor state error: %v", err)
			}
		}
	}()

	syncPipe, err := syncpipe.NewSyncPipe()
	if err != nil {
		return err
	}

	m.cmd.ExtraFiles = []*os.File{syncPipe.Child()}
	defer syncPipe.Close()

	log.Debugf("[MonitorProxy] run external monitor: %s : %s", m.cmd.Path, m.cmd.Args)
	if err := m.cmd.Start(); err != nil {
		return err
	}

	syncPipe.CloseChild()
	log.Debugf("[MonitorProxy] wait monitor start")
	// Sync with child, wait monitor started
	if err := syncPipe.ReadFromChild(); err != nil {
		m.cmd.Process.Kill()
		m.cmd.Wait()
		return err
	}
	log.Debugf("MonitorProxy monitor has started")

	// signal that the monitor process has started
	select {
	case <-m.startSignal:
	default:
		close(m.startSignal)
	}

	afterRun = true

	log.Debugf("[MonitorProxy] poll container status")
	if err := m.RunStatePoller(); err != nil {
		log.Errorf("run StatePoll failed: %v", err)
		m.cmd.Process.Kill()
		m.cmd.Wait()
		exitStatus = -10
		return err
	}

	log.Debugf("[MonitorProxy] wait monitor process exit")
	// wait monitor process
	if err := m.cmd.Wait(); err != nil {
		log.Errorf("[MonitorProxy] wait error: %v", err)
		exitStatus = -11
		return err
	}

	exitStatus = m.monitorCommand.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()

	log.Infof("[MonitorProxy] minitor process exited")
	return nil
}
Beispiel #4
0
// TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work.
// 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 *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console string, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
	var (
		err error
	)

	// create a pipe so that we can syncronize with the namespaced process and
	// pass the veth name to the child
	syncPipe, err := syncpipe.NewSyncPipe()
	if err != nil {
		return -1, err
	}
	defer syncPipe.Close()

	command := createCommand(container, console, rootfs, dataPath, os.Args[0], syncPipe.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

	if err := command.Start(); err != nil {
		return -1, err
	}

	// Now we passed the pipe to the child, close our side
	syncPipe.CloseChild()

	started, err := system.GetProcessStartTime(command.Process.Pid)
	if err != nil {
		return -1, err
	}

	// Do this before syncing with child so that no children
	// can escape the cgroup
	cgroupRef, err := SetupCgroups(container, command.Process.Pid)
	if err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}
	defer cgroupRef.Cleanup()

	cgroupPaths, err := cgroupRef.Paths()
	if err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}

	var networkState network.NetworkState
	if err := InitializeNetworking(container, command.Process.Pid, syncPipe, &networkState); err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}

	state := &libcontainer.State{
		InitPid:       command.Process.Pid,
		InitStartTime: started,
		NetworkState:  networkState,
		CgroupPaths:   cgroupPaths,
	}

	if err := libcontainer.SaveState(dataPath, state); err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}
	defer libcontainer.DeleteState(dataPath)

	// Sync with child
	if err := syncPipe.ReadFromChild(); err != nil {
		command.Process.Kill()
		command.Wait()
		return -1, err
	}

	if startCallback != nil {
		startCallback()
	}

	if err := command.Wait(); err != nil {
		if _, ok := err.(*exec.ExitError); !ok {
			return -1, err
		}
	}

	return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
}