mirror of
				https://github.com/opencontainers/runc.git
				synced 2025-10-31 11:06:21 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			331 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build linux
 | |
| 
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"syscall"
 | |
| 
 | |
| 	"github.com/Sirupsen/logrus"
 | |
| 	"github.com/codegangsta/cli"
 | |
| 	"github.com/opencontainers/runc/libcontainer"
 | |
| 	"github.com/opencontainers/runc/libcontainer/configs"
 | |
| 	"github.com/opencontainers/specs"
 | |
| )
 | |
| 
 | |
| const wildcard = -1
 | |
| 
 | |
| var errEmptyID = errors.New("container id cannot be empty")
 | |
| 
 | |
| var allowedDevices = []*configs.Device{
 | |
| 	// allow mknod for any device
 | |
| 	{
 | |
| 		Type:        'c',
 | |
| 		Major:       wildcard,
 | |
| 		Minor:       wildcard,
 | |
| 		Permissions: "m",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Type:        'b',
 | |
| 		Major:       wildcard,
 | |
| 		Minor:       wildcard,
 | |
| 		Permissions: "m",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Type:        'c',
 | |
| 		Path:        "/dev/null",
 | |
| 		Major:       1,
 | |
| 		Minor:       3,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Type:        'c',
 | |
| 		Path:        "/dev/random",
 | |
| 		Major:       1,
 | |
| 		Minor:       8,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Type:        'c',
 | |
| 		Path:        "/dev/full",
 | |
| 		Major:       1,
 | |
| 		Minor:       7,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Type:        'c',
 | |
| 		Path:        "/dev/tty",
 | |
| 		Major:       5,
 | |
| 		Minor:       0,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Type:        'c',
 | |
| 		Path:        "/dev/zero",
 | |
| 		Major:       1,
 | |
| 		Minor:       5,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Type:        'c',
 | |
| 		Path:        "/dev/urandom",
 | |
| 		Major:       1,
 | |
| 		Minor:       9,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Path:        "/dev/console",
 | |
| 		Type:        'c',
 | |
| 		Major:       5,
 | |
| 		Minor:       1,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Path:        "/dev/tty0",
 | |
| 		Type:        'c',
 | |
| 		Major:       4,
 | |
| 		Minor:       0,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Path:        "/dev/tty1",
 | |
| 		Type:        'c',
 | |
| 		Major:       4,
 | |
| 		Minor:       1,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	// /dev/pts/ - pts namespaces are "coming soon"
 | |
| 	{
 | |
| 		Path:        "",
 | |
| 		Type:        'c',
 | |
| 		Major:       136,
 | |
| 		Minor:       wildcard,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	{
 | |
| 		Path:        "",
 | |
| 		Type:        'c',
 | |
| 		Major:       5,
 | |
| 		Minor:       2,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| 	// tuntap
 | |
| 	{
 | |
| 		Path:        "",
 | |
| 		Type:        'c',
 | |
| 		Major:       10,
 | |
| 		Minor:       200,
 | |
| 		Permissions: "rwm",
 | |
| 		Allow:       true,
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var container libcontainer.Container
 | |
| 
 | |
| func containerPreload(context *cli.Context) error {
 | |
| 	c, err := getContainer(context)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	container = c
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // loadFactory returns the configured factory instance for execing containers.
 | |
| func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
 | |
| 	root := context.GlobalString("root")
 | |
| 	abs, err := filepath.Abs(root)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return libcontainer.New(abs, libcontainer.Cgroupfs, func(l *libcontainer.LinuxFactory) error {
 | |
| 		l.CriuPath = context.GlobalString("criu")
 | |
| 		return nil
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // getContainer returns the specified container instance by loading it from state
 | |
| // with the default factory.
 | |
| func getContainer(context *cli.Context) (libcontainer.Container, error) {
 | |
| 	id := context.Args().First()
 | |
| 	if id == "" {
 | |
| 		return nil, errEmptyID
 | |
| 	}
 | |
| 	factory, err := loadFactory(context)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return factory.Load(id)
 | |
| }
 | |
| 
 | |
| // fatal prints the error's details if it is a libcontainer specific error type
 | |
| // then exits the program with an exit status of 1.
 | |
| func fatal(err error) {
 | |
| 	if lerr, ok := err.(libcontainer.Error); ok {
 | |
| 		lerr.Detail(os.Stderr)
 | |
| 		os.Exit(1)
 | |
| 	}
 | |
| 	fmt.Fprintln(os.Stderr, err)
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| func getDefaultImagePath(context *cli.Context) string {
 | |
| 	cwd, err := os.Getwd()
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	return filepath.Join(cwd, "checkpoint")
 | |
| }
 | |
| 
 | |
| // newProcess returns a new libcontainer Process with the arguments from the
 | |
| // spec and stdio from the current process.
 | |
| func newProcess(p specs.Process) *libcontainer.Process {
 | |
| 	return &libcontainer.Process{
 | |
| 		Args: p.Args,
 | |
| 		Env:  p.Env,
 | |
| 		// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
 | |
| 		User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
 | |
| 		Cwd:  p.Cwd,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func dupStdio(process *libcontainer.Process, rootuid int) error {
 | |
| 	process.Stdin = os.Stdin
 | |
| 	process.Stdout = os.Stdout
 | |
| 	process.Stderr = os.Stderr
 | |
| 	for _, fd := range []uintptr{
 | |
| 		os.Stdin.Fd(),
 | |
| 		os.Stdout.Fd(),
 | |
| 		os.Stderr.Fd(),
 | |
| 	} {
 | |
| 		if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // If systemd is supporting sd_notify protocol, this function will add support
 | |
| // for sd_notify protocol from within the container.
 | |
| func setupSdNotify(spec *specs.LinuxSpec, notifySocket string) {
 | |
| 	spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
 | |
| 	spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
 | |
| }
 | |
| 
 | |
| func destroy(container libcontainer.Container) {
 | |
| 	if err := container.Destroy(); err != nil {
 | |
| 		logrus.Error(err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) {
 | |
| 	// detach and createTty will not work unless a console path is passed
 | |
| 	// so error out here before changing any terminal settings
 | |
| 	if createTTY && detach && console == "" {
 | |
| 		return nil, fmt.Errorf("cannot allocate tty if runc will detach")
 | |
| 	}
 | |
| 	if createTTY {
 | |
| 		return createTty(process, rootuid, console)
 | |
| 	}
 | |
| 	if detach {
 | |
| 		if err := dupStdio(process, rootuid); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 	return createStdioPipes(process, rootuid)
 | |
| }
 | |
| 
 | |
| func createPidFile(path string, process *libcontainer.Process) error {
 | |
| 	pid, err := process.Pid()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	f, err := os.Create(path)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer f.Close()
 | |
| 	_, err = fmt.Fprintf(f, "%d", pid)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func createContainer(context *cli.Context, id string, spec *specs.LinuxSpec) (libcontainer.Container, error) {
 | |
| 	config, err := createLibcontainerConfig(id, spec)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if _, err := os.Stat(config.Rootfs); err != nil {
 | |
| 		if os.IsNotExist(err) {
 | |
| 			return nil, fmt.Errorf("rootfs (%q) does not exist", config.Rootfs)
 | |
| 		}
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	factory, err := loadFactory(context)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return factory.Create(id, config)
 | |
| }
 | |
| 
 | |
| // runProcess will create a new process in the specified container
 | |
| // by executing the process specified in the 'config'.
 | |
| func runProcess(container libcontainer.Container, config *specs.Process, listenFDs []*os.File, console string, pidFile string, detach bool) (int, error) {
 | |
| 	process := newProcess(*config)
 | |
| 
 | |
| 	// Add extra file descriptors if needed
 | |
| 	if len(listenFDs) > 0 {
 | |
| 		process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(listenFDs)), "LISTEN_PID=1")
 | |
| 		process.ExtraFiles = append(process.ExtraFiles, listenFDs...)
 | |
| 	}
 | |
| 
 | |
| 	rootuid, err := container.Config().HostUID()
 | |
| 	if err != nil {
 | |
| 		return -1, err
 | |
| 	}
 | |
| 
 | |
| 	tty, err := setupIO(process, rootuid, console, config.Terminal, detach)
 | |
| 	if err != nil {
 | |
| 		return -1, err
 | |
| 	}
 | |
| 
 | |
| 	if err := container.Start(process); err != nil {
 | |
| 		tty.Close()
 | |
| 		return -1, err
 | |
| 	}
 | |
| 
 | |
| 	if pidFile != "" {
 | |
| 		if err := createPidFile(pidFile, process); err != nil {
 | |
| 			process.Signal(syscall.SIGKILL)
 | |
| 			process.Wait()
 | |
| 			tty.Close()
 | |
| 			return -1, err
 | |
| 		}
 | |
| 	}
 | |
| 	if detach {
 | |
| 		return 0, nil
 | |
| 	}
 | |
| 	handler := newSignalHandler(tty)
 | |
| 	defer handler.Close()
 | |
| 
 | |
| 	return handler.forward(process)
 | |
| }
 | 
