mirror of
				https://github.com/opencontainers/runc.git
				synced 2025-10-31 19:13:12 +08:00 
			
		
		
		
	 9b60a93cf3
			
		
	
	9b60a93cf3
	
	
	
		
			
			The userns package was moved to the moby/sys/userns module
at commit 3778ae603c.
This patch deprecates the old location, and adds it as an alias
for the moby/sys/userns package.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
	
		
			
				
	
	
		
			191 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strconv"
 | |
| 
 | |
| 	criu "github.com/checkpoint-restore/go-criu/v6/rpc"
 | |
| 	"github.com/moby/sys/userns"
 | |
| 	"github.com/opencontainers/runtime-spec/specs-go"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| 	"github.com/urfave/cli"
 | |
| 	"golang.org/x/sys/unix"
 | |
| 
 | |
| 	"github.com/opencontainers/runc/libcontainer"
 | |
| )
 | |
| 
 | |
| var checkpointCommand = cli.Command{
 | |
| 	Name:  "checkpoint",
 | |
| 	Usage: "checkpoint a running container",
 | |
| 	ArgsUsage: `<container-id>
 | |
| 
 | |
| Where "<container-id>" is the name for the instance of the container to be
 | |
| checkpointed.`,
 | |
| 	Description: `The checkpoint command saves the state of the container instance.`,
 | |
| 	Flags: []cli.Flag{
 | |
| 		cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
 | |
| 		cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
 | |
| 		cli.StringFlag{Name: "parent-path", Value: "", Usage: "path for previous criu image files in pre-dump"},
 | |
| 		cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
 | |
| 		cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
 | |
| 		cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
 | |
| 		cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
 | |
| 		cli.BoolFlag{Name: "lazy-pages", Usage: "use userfaultfd to lazily restore memory pages"},
 | |
| 		cli.IntFlag{Name: "status-fd", Value: -1, Usage: "criu writes \\0 to this FD once lazy-pages is ready"},
 | |
| 		cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
 | |
| 		cli.BoolFlag{Name: "file-locks", Usage: "handle file locks, for safety"},
 | |
| 		cli.BoolFlag{Name: "pre-dump", Usage: "dump container's memory information only, leave the container running after this"},
 | |
| 		cli.StringFlag{Name: "manage-cgroups-mode", Value: "", Usage: "cgroups mode: soft|full|strict|ignore (default: soft)"},
 | |
| 		cli.StringSliceFlag{Name: "empty-ns", Usage: "create a namespace, but don't restore its properties"},
 | |
| 		cli.BoolFlag{Name: "auto-dedup", Usage: "enable auto deduplication of memory images"},
 | |
| 	},
 | |
| 	Action: func(context *cli.Context) error {
 | |
| 		if err := checkArgs(context, 1, exactArgs); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		// XXX: Currently this is untested with rootless containers.
 | |
| 		if os.Geteuid() != 0 || userns.RunningInUserNS() {
 | |
| 			logrus.Warn("runc checkpoint is untested with rootless containers")
 | |
| 		}
 | |
| 
 | |
| 		container, err := getContainer(context)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		status, err := container.Status()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if status == libcontainer.Created || status == libcontainer.Stopped {
 | |
| 			return fmt.Errorf("Container cannot be checkpointed in %s state", status.String())
 | |
| 		}
 | |
| 		options, err := criuOptions(context)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		err = container.Checkpoint(options)
 | |
| 		if err == nil && !(options.LeaveRunning || options.PreDump) {
 | |
| 			// Destroy the container unless we tell CRIU to keep it.
 | |
| 			if err := container.Destroy(); err != nil {
 | |
| 				logrus.Warn(err)
 | |
| 			}
 | |
| 		}
 | |
| 		return err
 | |
| 	},
 | |
| }
 | |
| 
 | |
| func prepareImagePaths(context *cli.Context) (string, string, error) {
 | |
| 	imagePath := context.String("image-path")
 | |
| 	if imagePath == "" {
 | |
| 		imagePath = getDefaultImagePath()
 | |
| 	}
 | |
| 
 | |
| 	if err := os.MkdirAll(imagePath, 0o600); err != nil {
 | |
| 		return "", "", err
 | |
| 	}
 | |
| 
 | |
| 	parentPath := context.String("parent-path")
 | |
| 	if parentPath == "" {
 | |
| 		return imagePath, parentPath, nil
 | |
| 	}
 | |
| 
 | |
| 	if filepath.IsAbs(parentPath) {
 | |
| 		return "", "", errors.New("--parent-path must be relative")
 | |
| 	}
 | |
| 
 | |
| 	realParent := filepath.Join(imagePath, parentPath)
 | |
| 	fi, err := os.Stat(realParent)
 | |
| 	if err == nil && !fi.IsDir() {
 | |
| 		err = &os.PathError{Path: realParent, Err: unix.ENOTDIR}
 | |
| 	}
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return "", "", fmt.Errorf("invalid --parent-path: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	return imagePath, parentPath, nil
 | |
| }
 | |
| 
 | |
| func criuOptions(context *cli.Context) (*libcontainer.CriuOpts, error) {
 | |
| 	imagePath, parentPath, err := prepareImagePaths(context)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	opts := &libcontainer.CriuOpts{
 | |
| 		ImagesDirectory:         imagePath,
 | |
| 		WorkDirectory:           context.String("work-path"),
 | |
| 		ParentImage:             parentPath,
 | |
| 		LeaveRunning:            context.Bool("leave-running"),
 | |
| 		TcpEstablished:          context.Bool("tcp-established"),
 | |
| 		ExternalUnixConnections: context.Bool("ext-unix-sk"),
 | |
| 		ShellJob:                context.Bool("shell-job"),
 | |
| 		FileLocks:               context.Bool("file-locks"),
 | |
| 		PreDump:                 context.Bool("pre-dump"),
 | |
| 		AutoDedup:               context.Bool("auto-dedup"),
 | |
| 		LazyPages:               context.Bool("lazy-pages"),
 | |
| 		StatusFd:                context.Int("status-fd"),
 | |
| 		LsmProfile:              context.String("lsm-profile"),
 | |
| 		LsmMountContext:         context.String("lsm-mount-context"),
 | |
| 	}
 | |
| 
 | |
| 	// CRIU options below may or may not be set.
 | |
| 
 | |
| 	if psOpt := context.String("page-server"); psOpt != "" {
 | |
| 		address, port, err := net.SplitHostPort(psOpt)
 | |
| 
 | |
| 		if err != nil || address == "" || port == "" {
 | |
| 			return nil, errors.New("Use --page-server ADDRESS:PORT to specify page server")
 | |
| 		}
 | |
| 		portInt, err := strconv.Atoi(port)
 | |
| 		if err != nil {
 | |
| 			return nil, errors.New("Invalid port number")
 | |
| 		}
 | |
| 		opts.PageServer = libcontainer.CriuPageServerInfo{
 | |
| 			Address: address,
 | |
| 			Port:    int32(portInt),
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	switch context.String("manage-cgroups-mode") {
 | |
| 	case "":
 | |
| 		// do nothing
 | |
| 	case "soft":
 | |
| 		opts.ManageCgroupsMode = criu.CriuCgMode_SOFT
 | |
| 	case "full":
 | |
| 		opts.ManageCgroupsMode = criu.CriuCgMode_FULL
 | |
| 	case "strict":
 | |
| 		opts.ManageCgroupsMode = criu.CriuCgMode_STRICT
 | |
| 	case "ignore":
 | |
| 		opts.ManageCgroupsMode = criu.CriuCgMode_IGNORE
 | |
| 	default:
 | |
| 		return nil, errors.New("Invalid manage-cgroups-mode value")
 | |
| 	}
 | |
| 
 | |
| 	// runc doesn't manage network devices and their configuration.
 | |
| 	nsmask := unix.CLONE_NEWNET
 | |
| 
 | |
| 	if context.IsSet("empty-ns") {
 | |
| 		namespaceMapping := map[specs.LinuxNamespaceType]int{
 | |
| 			specs.NetworkNamespace: unix.CLONE_NEWNET,
 | |
| 		}
 | |
| 
 | |
| 		for _, ns := range context.StringSlice("empty-ns") {
 | |
| 			f, exists := namespaceMapping[specs.LinuxNamespaceType(ns)]
 | |
| 			if !exists {
 | |
| 				return nil, fmt.Errorf("namespace %q is not supported", ns)
 | |
| 			}
 | |
| 			nsmask |= f
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	opts.EmptyNs = uint32(nsmask)
 | |
| 
 | |
| 	return opts, nil
 | |
| }
 |