github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/runsc/container/container.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package container creates and manipulates containers.
    16  package container
    17  
    18  import (
    19  	"bufio"
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"os/exec"
    26  	"path"
    27  	"regexp"
    28  	"strconv"
    29  	"strings"
    30  	"syscall"
    31  	"time"
    32  
    33  	"github.com/cenkalti/backoff"
    34  	specs "github.com/opencontainers/runtime-spec/specs-go"
    35  	"golang.org/x/sys/unix"
    36  	"github.com/metacubex/gvisor/pkg/abi/linux"
    37  	"github.com/metacubex/gvisor/pkg/cleanup"
    38  	"github.com/metacubex/gvisor/pkg/log"
    39  	"github.com/metacubex/gvisor/pkg/sentry/control"
    40  	"github.com/metacubex/gvisor/pkg/sentry/fsimpl/erofs"
    41  	"github.com/metacubex/gvisor/pkg/sentry/fsimpl/tmpfs"
    42  	"github.com/metacubex/gvisor/pkg/sentry/pgalloc"
    43  	"github.com/metacubex/gvisor/pkg/sighandling"
    44  	"github.com/metacubex/gvisor/pkg/state/statefile"
    45  	"github.com/metacubex/gvisor/runsc/boot"
    46  	"github.com/metacubex/gvisor/runsc/cgroup"
    47  	"github.com/metacubex/gvisor/runsc/config"
    48  	"github.com/metacubex/gvisor/runsc/console"
    49  	"github.com/metacubex/gvisor/runsc/donation"
    50  	"github.com/metacubex/gvisor/runsc/sandbox"
    51  	"github.com/metacubex/gvisor/runsc/specutils"
    52  )
    53  
    54  const cgroupParentAnnotation = "dev.gvisor.spec.cgroup-parent"
    55  
    56  // validateID validates the container id.
    57  func validateID(id string) error {
    58  	// See libcontainer/factory_linux.go.
    59  	idRegex := regexp.MustCompile(`^[\w+\.-]+$`)
    60  	if !idRegex.MatchString(id) {
    61  		return fmt.Errorf("invalid container id: %v", id)
    62  	}
    63  	return nil
    64  }
    65  
    66  // Container represents a containerized application. When running, the
    67  // container is associated with a single Sandbox.
    68  //
    69  // Container metadata can be saved and loaded to disk. Within a root directory,
    70  // we maintain subdirectories for each container named with the container id.
    71  // The container metadata is stored as a json within the container directory
    72  // in a file named "meta.json". This metadata format is defined by us and is
    73  // not part of the OCI spec.
    74  //
    75  // Containers must write their metadata files after any change to their internal
    76  // states. The entire container directory is deleted when the container is
    77  // destroyed.
    78  //
    79  // When the container is stopped, all processes that belong to the container
    80  // must be stopped before Destroy() returns. containerd makes roughly the
    81  // following calls to stop a container:
    82  //   - First it attempts to kill the container process with
    83  //     'runsc kill SIGTERM'. After some time, it escalates to SIGKILL. In a
    84  //     separate thread, it's waiting on the container. As soon as the wait
    85  //     returns, it moves on to the next step:
    86  //   - It calls 'runsc kill --all SIGKILL' to stop every process that belongs to
    87  //     the container. 'kill --all SIGKILL' waits for all processes before
    88  //     returning.
    89  //   - Containerd waits for stdin, stdout and stderr to drain and be closed.
    90  //   - It calls 'runsc delete'. runc implementation kills --all SIGKILL once
    91  //     again just to be sure, waits, and then proceeds with remaining teardown.
    92  //
    93  // Container is thread-unsafe.
    94  type Container struct {
    95  	// ID is the container ID.
    96  	ID string `json:"id"`
    97  
    98  	// Spec is the OCI runtime spec that configures this container.
    99  	Spec *specs.Spec `json:"spec"`
   100  
   101  	// BundleDir is the directory containing the container bundle.
   102  	BundleDir string `json:"bundleDir"`
   103  
   104  	// CreatedAt is the time the container was created.
   105  	CreatedAt time.Time `json:"createdAt"`
   106  
   107  	// Owner is the container owner.
   108  	Owner string `json:"owner"`
   109  
   110  	// ConsoleSocket is the path to a unix domain socket that will receive
   111  	// the console FD.
   112  	ConsoleSocket string `json:"consoleSocket"`
   113  
   114  	// Status is the current container Status.
   115  	Status Status `json:"status"`
   116  
   117  	// GoferPid is the PID of the gofer running along side the sandbox. May
   118  	// be 0 if the gofer has been killed.
   119  	GoferPid int `json:"goferPid"`
   120  
   121  	// Sandbox is the sandbox this container is running in. It's set when the
   122  	// container is created and reset when the sandbox is destroyed.
   123  	Sandbox *sandbox.Sandbox `json:"sandbox"`
   124  
   125  	// CompatCgroup has the cgroup configuration for the container. For the single
   126  	// container case, container cgroup is set in `c.Sandbox` only. CompactCgroup
   127  	// is only set for multi-container, where the `c.Sandbox` cgroup represents
   128  	// the entire pod.
   129  	//
   130  	// Note that CompatCgroup is created only for compatibility with tools
   131  	// that expect container cgroups to exist. Setting limits here makes no change
   132  	// to the container in question.
   133  	CompatCgroup cgroup.CgroupJSON `json:"compatCgroup"`
   134  
   135  	// Saver handles load from/save to the state file safely from multiple
   136  	// processes.
   137  	Saver StateFile `json:"saver"`
   138  
   139  	// GoferMountConfs contains information about how the gofer mounts have been
   140  	// overlaid (with tmpfs or overlayfs). The first entry is for rootfs and the
   141  	// following entries are for bind mounts in Spec.Mounts (in the same order).
   142  	GoferMountConfs boot.GoferMountConfFlags `json:"goferMountConfs"`
   143  
   144  	//
   145  	// Fields below this line are not saved in the state file and will not
   146  	// be preserved across commands.
   147  	//
   148  
   149  	// goferIsChild is set if a gofer process is a child of the current process.
   150  	//
   151  	// This field isn't saved to json, because only a creator of a gofer
   152  	// process will have it as a child process.
   153  	goferIsChild bool `nojson:"true"`
   154  }
   155  
   156  // Args is used to configure a new container.
   157  type Args struct {
   158  	// ID is the container unique identifier.
   159  	ID string
   160  
   161  	// Spec is the OCI spec that describes the container.
   162  	Spec *specs.Spec
   163  
   164  	// BundleDir is the directory containing the container bundle.
   165  	BundleDir string
   166  
   167  	// ConsoleSocket is the path to a unix domain socket that will receive
   168  	// the console FD. It may be empty.
   169  	ConsoleSocket string
   170  
   171  	// PIDFile is the filename where the container's root process PID will be
   172  	// written to. It may be empty.
   173  	PIDFile string
   174  
   175  	// UserLog is the filename to send user-visible logs to. It may be empty.
   176  	//
   177  	// It only applies for the init container.
   178  	UserLog string
   179  
   180  	// Attached indicates that the sandbox lifecycle is attached with the caller.
   181  	// If the caller exits, the sandbox should exit too.
   182  	//
   183  	// It only applies for the init container.
   184  	Attached bool
   185  
   186  	// PassFiles are user-supplied files from the host to be exposed to the
   187  	// sandboxed app.
   188  	PassFiles map[int]*os.File
   189  
   190  	// ExecFile is the host file used for program execution.
   191  	ExecFile *os.File
   192  }
   193  
   194  // New creates the container in a new Sandbox process, unless the metadata
   195  // indicates that an existing Sandbox should be used. The caller must call
   196  // Destroy() on the container.
   197  func New(conf *config.Config, args Args) (*Container, error) {
   198  	log.Debugf("Create container, cid: %s, rootDir: %q", args.ID, conf.RootDir)
   199  	if err := validateID(args.ID); err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	if err := os.MkdirAll(conf.RootDir, 0711); err != nil {
   204  		return nil, fmt.Errorf("creating container root directory %q: %v", conf.RootDir, err)
   205  	}
   206  
   207  	if err := modifySpecForDirectfs(conf, args.Spec); err != nil {
   208  		return nil, fmt.Errorf("failed to modify spec for directfs: %v", err)
   209  	}
   210  
   211  	sandboxID := args.ID
   212  	if !isRoot(args.Spec) {
   213  		var ok bool
   214  		sandboxID, ok = specutils.SandboxID(args.Spec)
   215  		if !ok {
   216  			return nil, fmt.Errorf("no sandbox ID found when creating container")
   217  		}
   218  	}
   219  
   220  	c := &Container{
   221  		ID:            args.ID,
   222  		Spec:          args.Spec,
   223  		ConsoleSocket: args.ConsoleSocket,
   224  		BundleDir:     args.BundleDir,
   225  		Status:        Creating,
   226  		CreatedAt:     time.Now(),
   227  		Owner:         os.Getenv("USER"),
   228  		Saver: StateFile{
   229  			RootDir: conf.RootDir,
   230  			ID: FullID{
   231  				SandboxID:   sandboxID,
   232  				ContainerID: args.ID,
   233  			},
   234  		},
   235  	}
   236  	// The Cleanup object cleans up partially created containers when an error
   237  	// occurs. Any errors occurring during cleanup itself are ignored.
   238  	cu := cleanup.Make(func() { _ = c.Destroy() })
   239  	defer cu.Clean()
   240  
   241  	// Lock the container metadata file to prevent concurrent creations of
   242  	// containers with the same id.
   243  	if err := c.Saver.LockForNew(); err != nil {
   244  		return nil, fmt.Errorf("cannot lock container metadata file: %w", err)
   245  	}
   246  	defer c.Saver.UnlockOrDie()
   247  
   248  	// If the metadata annotations indicate that this container should be started
   249  	// in an existing sandbox, we must do so. These are the possible metadata
   250  	// annotation states:
   251  	//   1. No annotations: it means that there is a single container and this
   252  	//      container is obviously the root. Both container and sandbox share the
   253  	//      ID.
   254  	//   2. Container type == sandbox: it means this is the root container
   255  	//  		starting the sandbox. Both container and sandbox share the same ID.
   256  	//   3. Container type == container: it means this is a subcontainer of an
   257  	//      already started sandbox. In this case, container ID is different than
   258  	//      the sandbox ID.
   259  	if isRoot(args.Spec) {
   260  		log.Debugf("Creating new sandbox for container, cid: %s", args.ID)
   261  
   262  		if args.Spec.Linux == nil {
   263  			args.Spec.Linux = &specs.Linux{}
   264  		}
   265  		// Don't force the use of cgroups in tests because they lack permission to do so.
   266  		if args.Spec.Linux.CgroupsPath == "" && !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
   267  			args.Spec.Linux.CgroupsPath = "/" + args.ID
   268  		}
   269  		var subCgroup, parentCgroup, containerCgroup cgroup.Cgroup
   270  		if !conf.IgnoreCgroups {
   271  			var err error
   272  
   273  			// Create and join cgroup before processes are created to ensure they are
   274  			// part of the cgroup from the start (and all their children processes).
   275  			parentCgroup, subCgroup, err = c.setupCgroupForRoot(conf, args.Spec)
   276  			if err != nil {
   277  				return nil, fmt.Errorf("cannot set up cgroup for root: %w", err)
   278  			}
   279  			// Join the child cgroup when using cgroupfs. Joining non leaf-node
   280  			// cgroups is illegal in cgroupsv2 and will return EBUSY.
   281  			if subCgroup != nil && !conf.SystemdCgroup && cgroup.IsOnlyV2() {
   282  				containerCgroup = subCgroup
   283  			} else {
   284  				containerCgroup = parentCgroup
   285  			}
   286  		}
   287  		c.CompatCgroup = cgroup.CgroupJSON{Cgroup: subCgroup}
   288  		mountHints, err := boot.NewPodMountHints(args.Spec)
   289  		if err != nil {
   290  			return nil, fmt.Errorf("error creating pod mount hints: %w", err)
   291  		}
   292  		rootfsHint, err := boot.NewRootfsHint(args.Spec)
   293  		if err != nil {
   294  			return nil, fmt.Errorf("error creating rootfs hint: %w", err)
   295  		}
   296  		goferFilestores, goferConfs, err := c.createGoferFilestores(conf.GetOverlay2(), mountHints, rootfsHint)
   297  		if err != nil {
   298  			return nil, err
   299  		}
   300  		if !goferConfs[0].ShouldUseLisafs() && specutils.GPUFunctionalityRequestedViaHook(args.Spec, conf) {
   301  			// nvidia-container-runtime-hook attempts to populate the container
   302  			// rootfs with NVIDIA libraries and devices. With EROFS, spec.Root.Path
   303  			// points to an empty directory and populating that has no effect.
   304  			return nil, fmt.Errorf("nvidia-container-runtime-hook cannot be used together with non-lisafs backed root mount")
   305  		}
   306  		c.GoferMountConfs = goferConfs
   307  		if err := nvProxyPreGoferHostSetup(args.Spec, conf); err != nil {
   308  			return nil, err
   309  		}
   310  		if err := runInCgroup(containerCgroup, func() error {
   311  			ioFiles, devIOFile, specFile, err := c.createGoferProcess(args.Spec, conf, args.BundleDir, args.Attached, rootfsHint)
   312  			if err != nil {
   313  				return fmt.Errorf("cannot create gofer process: %w", err)
   314  			}
   315  
   316  			// Start a new sandbox for this container. Any errors after this point
   317  			// must destroy the container.
   318  			sandArgs := &sandbox.Args{
   319  				ID:                  sandboxID,
   320  				Spec:                args.Spec,
   321  				BundleDir:           args.BundleDir,
   322  				ConsoleSocket:       args.ConsoleSocket,
   323  				UserLog:             args.UserLog,
   324  				IOFiles:             ioFiles,
   325  				DevIOFile:           devIOFile,
   326  				MountsFile:          specFile,
   327  				Cgroup:              containerCgroup,
   328  				Attached:            args.Attached,
   329  				GoferFilestoreFiles: goferFilestores,
   330  				GoferMountConfs:     goferConfs,
   331  				MountHints:          mountHints,
   332  				PassFiles:           args.PassFiles,
   333  				ExecFile:            args.ExecFile,
   334  			}
   335  			sand, err := sandbox.New(conf, sandArgs)
   336  			if err != nil {
   337  				return fmt.Errorf("cannot create sandbox: %w", err)
   338  			}
   339  			c.Sandbox = sand
   340  			return nil
   341  
   342  		}); err != nil {
   343  			return nil, err
   344  		}
   345  	} else {
   346  		log.Debugf("Creating new container, cid: %s, sandbox: %s", c.ID, sandboxID)
   347  
   348  		// Find the sandbox associated with this ID.
   349  		fullID := FullID{
   350  			SandboxID:   sandboxID,
   351  			ContainerID: sandboxID,
   352  		}
   353  		sb, err := Load(conf.RootDir, fullID, LoadOpts{Exact: true})
   354  		if err != nil {
   355  			return nil, fmt.Errorf("cannot load sandbox: %w", err)
   356  		}
   357  		c.Sandbox = sb.Sandbox
   358  
   359  		subCgroup, err := c.setupCgroupForSubcontainer(conf, args.Spec)
   360  		if err != nil {
   361  			return nil, err
   362  		}
   363  		c.CompatCgroup = cgroup.CgroupJSON{Cgroup: subCgroup}
   364  
   365  		// If the console control socket file is provided, then create a new
   366  		// pty master/slave pair and send the TTY to the sandbox process.
   367  		var tty *os.File
   368  		if c.ConsoleSocket != "" {
   369  			// Create a new TTY pair and send the master on the provided socket.
   370  			var err error
   371  			tty, err = console.NewWithSocket(c.ConsoleSocket)
   372  			if err != nil {
   373  				return nil, fmt.Errorf("setting up console with socket %q: %w", c.ConsoleSocket, err)
   374  			}
   375  			// tty file is transferred to the sandbox, then it can be closed here.
   376  			defer tty.Close()
   377  		}
   378  
   379  		if err := c.Sandbox.CreateSubcontainer(conf, c.ID, tty); err != nil {
   380  			return nil, fmt.Errorf("cannot create subcontainer: %w", err)
   381  		}
   382  	}
   383  	c.changeStatus(Created)
   384  
   385  	// Save the metadata file.
   386  	if err := c.saveLocked(); err != nil {
   387  		return nil, err
   388  	}
   389  
   390  	// "If any prestart hook fails, the runtime MUST generate an error,
   391  	// stop and destroy the container" -OCI spec.
   392  	if c.Spec.Hooks != nil {
   393  		// Even though the hook name is Prestart, runc used to call it from create.
   394  		// For this reason, it's now deprecated, but the spec requires it to be
   395  		// called *before* CreateRuntime and CreateRuntime must be called in create.
   396  		//
   397  		// "For runtimes that implement the deprecated prestart hooks as
   398  		// createRuntime hooks, createRuntime hooks MUST be called after the
   399  		// prestart hooks."
   400  		if err := executeHooks(c.Spec.Hooks.Prestart, c.State()); err != nil {
   401  			return nil, err
   402  		}
   403  		if err := executeHooks(c.Spec.Hooks.CreateRuntime, c.State()); err != nil {
   404  			return nil, err
   405  		}
   406  		if len(c.Spec.Hooks.CreateContainer) > 0 {
   407  			log.Warningf("CreateContainer hook skipped because running inside container namespace is not supported")
   408  		}
   409  	}
   410  
   411  	// Write the PID file. Containerd considers the call to create complete after
   412  	// this file is created, so it must be the last thing we do.
   413  	if args.PIDFile != "" {
   414  		if err := ioutil.WriteFile(args.PIDFile, []byte(strconv.Itoa(c.SandboxPid())), 0644); err != nil {
   415  			return nil, fmt.Errorf("error writing PID file: %v", err)
   416  		}
   417  	}
   418  
   419  	cu.Release()
   420  	return c, nil
   421  }
   422  
   423  // Start starts running the containerized process inside the sandbox.
   424  func (c *Container) Start(conf *config.Config) error {
   425  	log.Debugf("Start container, cid: %s", c.ID)
   426  
   427  	if err := c.Saver.lock(BlockAcquire); err != nil {
   428  		return err
   429  	}
   430  	unlock := cleanup.Make(c.Saver.UnlockOrDie)
   431  	defer unlock.Clean()
   432  
   433  	if err := c.requireStatus("start", Created); err != nil {
   434  		return err
   435  	}
   436  
   437  	// "If any prestart hook fails, the runtime MUST generate an error,
   438  	// stop and destroy the container" -OCI spec.
   439  	if c.Spec.Hooks != nil && len(c.Spec.Hooks.StartContainer) > 0 {
   440  		log.Warningf("StartContainer hook skipped because running inside container namespace is not supported")
   441  	}
   442  
   443  	if isRoot(c.Spec) {
   444  		if err := c.Sandbox.StartRoot(conf); err != nil {
   445  			return err
   446  		}
   447  	} else {
   448  		rootfsHint, err := boot.NewRootfsHint(c.Spec)
   449  		if err != nil {
   450  			return fmt.Errorf("error creating rootfs hint: %w", err)
   451  		}
   452  		goferFilestores, goferConfs, err := c.createGoferFilestores(conf.GetOverlay2(), c.Sandbox.MountHints, rootfsHint)
   453  		if err != nil {
   454  			return err
   455  		}
   456  		c.GoferMountConfs = goferConfs
   457  		// Join cgroup to start gofer process to ensure it's part of the cgroup from
   458  		// the start (and all their children processes).
   459  		if err := runInCgroup(c.Sandbox.CgroupJSON.Cgroup, func() error {
   460  			// Create the gofer process.
   461  			goferFiles, devIOFile, mountsFile, err := c.createGoferProcess(c.Spec, conf, c.BundleDir, false, rootfsHint)
   462  			if err != nil {
   463  				return err
   464  			}
   465  			defer func() {
   466  				if mountsFile != nil {
   467  					_ = mountsFile.Close()
   468  				}
   469  				if devIOFile != nil {
   470  					_ = devIOFile.Close()
   471  				}
   472  				for _, f := range goferFiles {
   473  					_ = f.Close()
   474  				}
   475  				for _, f := range goferFilestores {
   476  					_ = f.Close()
   477  				}
   478  			}()
   479  
   480  			if mountsFile != nil {
   481  				cleanMounts, err := specutils.ReadMounts(mountsFile)
   482  				if err != nil {
   483  					return fmt.Errorf("reading mounts file: %v", err)
   484  				}
   485  				c.Spec.Mounts = cleanMounts
   486  			}
   487  
   488  			// Setup stdios if the container is not using terminal. Otherwise TTY was
   489  			// already setup in create.
   490  			var stdios []*os.File
   491  			if !c.Spec.Process.Terminal {
   492  				stdios = []*os.File{os.Stdin, os.Stdout, os.Stderr}
   493  			}
   494  
   495  			return c.Sandbox.StartSubcontainer(c.Spec, conf, c.ID, stdios, goferFiles, goferFilestores, devIOFile, goferConfs)
   496  		}); err != nil {
   497  			return err
   498  		}
   499  	}
   500  
   501  	// "If any poststart hook fails, the runtime MUST log a warning, but
   502  	// the remaining hooks and lifecycle continue as if the hook had
   503  	// succeeded" -OCI spec.
   504  	if c.Spec.Hooks != nil {
   505  		executeHooksBestEffort(c.Spec.Hooks.Poststart, c.State())
   506  	}
   507  
   508  	c.changeStatus(Running)
   509  	if err := c.saveLocked(); err != nil {
   510  		return err
   511  	}
   512  
   513  	// Release lock before adjusting OOM score because the lock is acquired there.
   514  	unlock.Clean()
   515  
   516  	// Adjust the oom_score_adj for sandbox. This must be done after saveLocked().
   517  	if err := adjustSandboxOOMScoreAdj(c.Sandbox, c.Spec, c.Saver.RootDir, false); err != nil {
   518  		return err
   519  	}
   520  
   521  	// Set container's oom_score_adj to the gofer since it is dedicated to
   522  	// the container, in case the gofer uses up too much memory.
   523  	return c.adjustGoferOOMScoreAdj()
   524  }
   525  
   526  // Restore takes a container and replaces its kernel and file system
   527  // to restore a container from its state file.
   528  func (c *Container) Restore(conf *config.Config, restoreFile string) error {
   529  	log.Debugf("Restore container, cid: %s", c.ID)
   530  	if err := c.Saver.lock(BlockAcquire); err != nil {
   531  		return err
   532  	}
   533  	defer c.Saver.UnlockOrDie()
   534  
   535  	if err := c.requireStatus("restore", Created); err != nil {
   536  		return err
   537  	}
   538  
   539  	// "If any prestart hook fails, the runtime MUST generate an error,
   540  	// stop and destroy the container" -OCI spec.
   541  	if c.Spec.Hooks != nil && len(c.Spec.Hooks.StartContainer) > 0 {
   542  		log.Warningf("StartContainer hook skipped because running inside container namespace is not supported")
   543  	}
   544  
   545  	if err := c.Sandbox.Restore(conf, c.ID, restoreFile); err != nil {
   546  		return err
   547  	}
   548  	c.changeStatus(Running)
   549  	return c.saveLocked()
   550  }
   551  
   552  // Run is a helper that calls Create + Start + Wait.
   553  func Run(conf *config.Config, args Args) (unix.WaitStatus, error) {
   554  	log.Debugf("Run container, cid: %s, rootDir: %q", args.ID, conf.RootDir)
   555  	c, err := New(conf, args)
   556  	if err != nil {
   557  		return 0, fmt.Errorf("creating container: %v", err)
   558  	}
   559  	// Clean up partially created container if an error occurs.
   560  	// Any errors returned by Destroy() itself are ignored.
   561  	cu := cleanup.Make(func() {
   562  		c.Destroy()
   563  	})
   564  	defer cu.Clean()
   565  
   566  	if conf.RestoreFile != "" {
   567  		log.Debugf("Restore: %v", conf.RestoreFile)
   568  		if err := c.Restore(conf, conf.RestoreFile); err != nil {
   569  			return 0, fmt.Errorf("starting container: %v", err)
   570  		}
   571  	} else {
   572  		if err := c.Start(conf); err != nil {
   573  			return 0, fmt.Errorf("starting container: %v", err)
   574  		}
   575  	}
   576  
   577  	// If we allocate a terminal, forward signals to the sandbox process.
   578  	// Otherwise, Ctrl+C will terminate this process and its children,
   579  	// including the terminal.
   580  	if c.Spec.Process.Terminal {
   581  		stopForwarding := c.ForwardSignals(0, true /* fgProcess */)
   582  		defer stopForwarding()
   583  	}
   584  
   585  	if args.Attached {
   586  		return c.Wait()
   587  	}
   588  	cu.Release()
   589  	return 0, nil
   590  }
   591  
   592  // Execute runs the specified command in the container. It returns the PID of
   593  // the newly created process.
   594  func (c *Container) Execute(conf *config.Config, args *control.ExecArgs) (int32, error) {
   595  	log.Debugf("Execute in container, cid: %s, args: %+v", c.ID, args)
   596  	if err := c.requireStatus("execute in", Created, Running); err != nil {
   597  		return 0, err
   598  	}
   599  	args.ContainerID = c.ID
   600  	return c.Sandbox.Execute(conf, args)
   601  }
   602  
   603  // Event returns events for the container.
   604  func (c *Container) Event() (*boot.EventOut, error) {
   605  	log.Debugf("Getting events for container, cid: %s", c.ID)
   606  	if err := c.requireStatus("get events for", Created, Running, Paused); err != nil {
   607  		return nil, err
   608  	}
   609  	event, err := c.Sandbox.Event(c.ID)
   610  	if err != nil {
   611  		return nil, err
   612  	}
   613  
   614  	if len(event.ContainerUsage) > 0 {
   615  		// Some stats can utilize host cgroups for accuracy.
   616  		c.populateStats(event)
   617  	}
   618  
   619  	return event, nil
   620  }
   621  
   622  // PortForward starts port forwarding to the container.
   623  func (c *Container) PortForward(opts *boot.PortForwardOpts) error {
   624  	if err := c.requireStatus("port forward", Running); err != nil {
   625  		return err
   626  	}
   627  	opts.ContainerID = c.ID
   628  	return c.Sandbox.PortForward(opts)
   629  }
   630  
   631  // SandboxPid returns the Getpid of the sandbox the container is running in, or -1 if the
   632  // container is not running.
   633  func (c *Container) SandboxPid() int {
   634  	if err := c.requireStatus("get PID", Created, Running, Paused); err != nil {
   635  		return -1
   636  	}
   637  	return c.Sandbox.Getpid()
   638  }
   639  
   640  // Wait waits for the container to exit, and returns its WaitStatus.
   641  // Call to wait on a stopped container is needed to retrieve the exit status
   642  // and wait returns immediately.
   643  func (c *Container) Wait() (unix.WaitStatus, error) {
   644  	log.Debugf("Wait on container, cid: %s", c.ID)
   645  	ws, err := c.Sandbox.Wait(c.ID)
   646  	if err == nil {
   647  		// Wait succeeded, container is not running anymore.
   648  		c.changeStatus(Stopped)
   649  	}
   650  	return ws, err
   651  }
   652  
   653  // WaitRootPID waits for process 'pid' in the sandbox's PID namespace and
   654  // returns its WaitStatus.
   655  func (c *Container) WaitRootPID(pid int32) (unix.WaitStatus, error) {
   656  	log.Debugf("Wait on process %d in sandbox, cid: %s", pid, c.Sandbox.ID)
   657  	if !c.IsSandboxRunning() {
   658  		return 0, fmt.Errorf("sandbox is not running")
   659  	}
   660  	return c.Sandbox.WaitPID(c.Sandbox.ID, pid)
   661  }
   662  
   663  // WaitPID waits for process 'pid' in the container's PID namespace and returns
   664  // its WaitStatus.
   665  func (c *Container) WaitPID(pid int32) (unix.WaitStatus, error) {
   666  	log.Debugf("Wait on process %d in container, cid: %s", pid, c.ID)
   667  	if !c.IsSandboxRunning() {
   668  		return 0, fmt.Errorf("sandbox is not running")
   669  	}
   670  	return c.Sandbox.WaitPID(c.ID, pid)
   671  }
   672  
   673  // SignalContainer sends the signal to the container. If all is true and signal
   674  // is SIGKILL, then waits for all processes to exit before returning.
   675  // SignalContainer returns an error if the container is already stopped.
   676  // TODO(b/113680494): Distinguish different error types.
   677  func (c *Container) SignalContainer(sig unix.Signal, all bool) error {
   678  	log.Debugf("Signal container, cid: %s, signal: %v (%d)", c.ID, sig, sig)
   679  	// Signaling container in Stopped state is allowed. When all=false,
   680  	// an error will be returned anyway; when all=true, this allows
   681  	// sending signal to other processes inside the container even
   682  	// after the init process exits. This is especially useful for
   683  	// container cleanup.
   684  	if err := c.requireStatus("signal", Running, Stopped); err != nil {
   685  		return err
   686  	}
   687  	if !c.IsSandboxRunning() {
   688  		return fmt.Errorf("sandbox is not running")
   689  	}
   690  	return c.Sandbox.SignalContainer(c.ID, sig, all)
   691  }
   692  
   693  // SignalProcess sends sig to a specific process in the container.
   694  func (c *Container) SignalProcess(sig unix.Signal, pid int32) error {
   695  	log.Debugf("Signal process %d in container, cid: %s, signal: %v (%d)", pid, c.ID, sig, sig)
   696  	if err := c.requireStatus("signal a process inside", Running); err != nil {
   697  		return err
   698  	}
   699  	if !c.IsSandboxRunning() {
   700  		return fmt.Errorf("sandbox is not running")
   701  	}
   702  	return c.Sandbox.SignalProcess(c.ID, int32(pid), sig, false)
   703  }
   704  
   705  // ForwardSignals forwards all signals received by the current process to the
   706  // container process inside the sandbox. It returns a function that will stop
   707  // forwarding signals.
   708  func (c *Container) ForwardSignals(pid int32, fgProcess bool) func() {
   709  	log.Debugf("Forwarding all signals to container, cid: %s, PIDPID: %d, fgProcess: %t", c.ID, pid, fgProcess)
   710  	stop := sighandling.StartSignalForwarding(func(sig linux.Signal) {
   711  		log.Debugf("Forwarding signal %d to container, cid: %s, PID: %d, fgProcess: %t", sig, c.ID, pid, fgProcess)
   712  		if err := c.Sandbox.SignalProcess(c.ID, pid, unix.Signal(sig), fgProcess); err != nil {
   713  			log.Warningf("error forwarding signal %d to container %q: %v", sig, c.ID, err)
   714  		}
   715  	})
   716  	return func() {
   717  		log.Debugf("Done forwarding signals to container, cid: %s, PID: %d, fgProcess: %t", c.ID, pid, fgProcess)
   718  		stop()
   719  	}
   720  }
   721  
   722  // Checkpoint sends the checkpoint call to the container.
   723  // The statefile will be written to f, the file at the specified image-path.
   724  func (c *Container) Checkpoint(f *os.File, options statefile.Options) error {
   725  	log.Debugf("Checkpoint container, cid: %s", c.ID)
   726  	if err := c.requireStatus("checkpoint", Created, Running, Paused); err != nil {
   727  		return err
   728  	}
   729  	return c.Sandbox.Checkpoint(c.ID, f, options)
   730  }
   731  
   732  // Pause suspends the container and its kernel.
   733  // The call only succeeds if the container's status is created or running.
   734  func (c *Container) Pause() error {
   735  	log.Debugf("Pausing container, cid: %s", c.ID)
   736  	if err := c.Saver.lock(BlockAcquire); err != nil {
   737  		return err
   738  	}
   739  	defer c.Saver.UnlockOrDie()
   740  
   741  	if c.Status != Created && c.Status != Running {
   742  		return fmt.Errorf("cannot pause container %q in state %v", c.ID, c.Status)
   743  	}
   744  
   745  	if err := c.Sandbox.Pause(c.ID); err != nil {
   746  		return fmt.Errorf("pausing container %q: %v", c.ID, err)
   747  	}
   748  	c.changeStatus(Paused)
   749  	return c.saveLocked()
   750  }
   751  
   752  // Resume unpauses the container and its kernel.
   753  // The call only succeeds if the container's status is paused.
   754  func (c *Container) Resume() error {
   755  	log.Debugf("Resuming container, cid: %s", c.ID)
   756  	if err := c.Saver.lock(BlockAcquire); err != nil {
   757  		return err
   758  	}
   759  	defer c.Saver.UnlockOrDie()
   760  
   761  	if c.Status != Paused {
   762  		return fmt.Errorf("cannot resume container %q in state %v", c.ID, c.Status)
   763  	}
   764  	if err := c.Sandbox.Resume(c.ID); err != nil {
   765  		return fmt.Errorf("resuming container: %v", err)
   766  	}
   767  	c.changeStatus(Running)
   768  	return c.saveLocked()
   769  }
   770  
   771  // State returns the metadata of the container.
   772  func (c *Container) State() specs.State {
   773  	return specs.State{
   774  		Version:     specs.Version,
   775  		ID:          c.ID,
   776  		Status:      c.Status,
   777  		Pid:         c.SandboxPid(),
   778  		Bundle:      c.BundleDir,
   779  		Annotations: c.Spec.Annotations,
   780  	}
   781  }
   782  
   783  // Processes retrieves the list of processes and associated metadata inside a
   784  // container.
   785  func (c *Container) Processes() ([]*control.Process, error) {
   786  	if err := c.requireStatus("get processes of", Running, Paused); err != nil {
   787  		return nil, err
   788  	}
   789  	return c.Sandbox.Processes(c.ID)
   790  }
   791  
   792  // Destroy stops all processes and frees all resources associated with the
   793  // container.
   794  func (c *Container) Destroy() error {
   795  	log.Debugf("Destroy container, cid: %s", c.ID)
   796  
   797  	if err := c.Saver.lock(BlockAcquire); err != nil {
   798  		return err
   799  	}
   800  	defer func() {
   801  		c.Saver.UnlockOrDie()
   802  		_ = c.Saver.close()
   803  	}()
   804  
   805  	// Stored for later use as stop() sets c.Sandbox to nil.
   806  	sb := c.Sandbox
   807  
   808  	// We must perform the following cleanup steps:
   809  	//	* stop the container and gofer processes,
   810  	//	* remove the container filesystem on the host, and
   811  	//	* delete the container metadata directory.
   812  	//
   813  	// It's possible for one or more of these steps to fail, but we should
   814  	// do our best to perform all of the cleanups. Hence, we keep a slice
   815  	// of errors return their concatenation.
   816  	var errs []string
   817  	if err := c.stop(); err != nil {
   818  		err = fmt.Errorf("stopping container: %v", err)
   819  		log.Warningf("%v", err)
   820  		errs = append(errs, err.Error())
   821  	}
   822  
   823  	if err := c.Saver.Destroy(); err != nil {
   824  		err = fmt.Errorf("deleting container state files: %v", err)
   825  		log.Warningf("%v", err)
   826  		errs = append(errs, err.Error())
   827  	}
   828  
   829  	// Clean up self-backed filestore files created in their respective mounts.
   830  	c.forEachSelfMount(func(mountSrc string) {
   831  		if sb != nil {
   832  			if hint := sb.MountHints.FindMount(mountSrc); hint != nil && hint.ShouldShareMount() {
   833  				// Don't delete filestore file for shared mounts. The sandbox owns a
   834  				// shared master mount which uses this filestore and is shared with
   835  				// multiple mount points.
   836  				return
   837  			}
   838  		}
   839  		filestorePath := boot.SelfFilestorePath(mountSrc, c.sandboxID())
   840  		if err := os.Remove(filestorePath); err != nil {
   841  			err = fmt.Errorf("failed to delete filestore file %q: %v", filestorePath, err)
   842  			log.Warningf("%v", err)
   843  			errs = append(errs, err.Error())
   844  		}
   845  	})
   846  	if sb != nil && sb.IsRootContainer(c.ID) {
   847  		// When the root container is being destroyed, we can clean up filestores
   848  		// used by shared mounts.
   849  		for _, hint := range sb.MountHints.Mounts {
   850  			if !hint.ShouldShareMount() {
   851  				continue
   852  			}
   853  			// Assume this is a self-backed shared mount and try to delete the
   854  			// filestore. Subsequently ignore the ENOENT if the assumption is wrong.
   855  			filestorePath := boot.SelfFilestorePath(hint.Mount.Source, c.sandboxID())
   856  			if err := os.Remove(filestorePath); err != nil && !os.IsNotExist(err) {
   857  				err = fmt.Errorf("failed to delete shared filestore file %q: %v", filestorePath, err)
   858  				log.Warningf("%v", err)
   859  				errs = append(errs, err.Error())
   860  			}
   861  		}
   862  	}
   863  
   864  	c.changeStatus(Stopped)
   865  
   866  	// Adjust oom_score_adj for the sandbox. This must be done after the container
   867  	// is stopped and the directory at c.Root is removed.
   868  	//
   869  	// Use 'sb' to tell whether it has been executed before because Destroy must
   870  	// be idempotent.
   871  	if sb != nil {
   872  		if err := adjustSandboxOOMScoreAdj(sb, c.Spec, c.Saver.RootDir, true); err != nil {
   873  			errs = append(errs, err.Error())
   874  		}
   875  	}
   876  
   877  	// "If any poststop hook fails, the runtime MUST log a warning, but the
   878  	// remaining hooks and lifecycle continue as if the hook had
   879  	// succeeded" - OCI spec.
   880  	//
   881  	// Based on the OCI, "The post-stop hooks MUST be called after the container
   882  	// is deleted but before the delete operation returns"
   883  	// Run it here to:
   884  	// 1) Conform to the OCI.
   885  	// 2) Make sure it only runs once, because the root has been deleted, the
   886  	// container can't be loaded again.
   887  	if c.Spec.Hooks != nil {
   888  		executeHooksBestEffort(c.Spec.Hooks.Poststop, c.State())
   889  	}
   890  
   891  	if len(errs) == 0 {
   892  		return nil
   893  	}
   894  	return fmt.Errorf(strings.Join(errs, "\n"))
   895  }
   896  
   897  func (c *Container) sandboxID() string {
   898  	return c.Saver.ID.SandboxID
   899  }
   900  
   901  func (c *Container) forEachSelfMount(fn func(mountSrc string)) {
   902  	if c.GoferMountConfs == nil {
   903  		// Container not started? Skip.
   904  		return
   905  	}
   906  	if c.GoferMountConfs[0].IsSelfBacked() {
   907  		fn(c.Spec.Root.Path)
   908  	}
   909  	goferMntIdx := 1 // First index is for rootfs.
   910  	for i := range c.Spec.Mounts {
   911  		if !specutils.IsGoferMount(c.Spec.Mounts[i]) {
   912  			continue
   913  		}
   914  		if c.GoferMountConfs[goferMntIdx].IsSelfBacked() {
   915  			fn(c.Spec.Mounts[i].Source)
   916  		}
   917  		goferMntIdx++
   918  	}
   919  }
   920  
   921  // createGoferFilestores creates the regular files that will back the
   922  // tmpfs/overlayfs mounts that will overlay some gofer mounts. It also returns
   923  // information about how each gofer mount is configured.
   924  func (c *Container) createGoferFilestores(ovlConf config.Overlay2, mountHints *boot.PodMountHints, rootfsHint *boot.RootfsHint) ([]*os.File, []boot.GoferMountConf, error) {
   925  	var goferFilestores []*os.File
   926  	var goferConfs []boot.GoferMountConf
   927  
   928  	// Handle root mount first.
   929  	overlayMedium := ovlConf.RootOverlayMedium()
   930  	mountType := boot.Bind
   931  	if rootfsHint != nil {
   932  		overlayMedium = rootfsHint.Overlay
   933  		if !specutils.IsGoferMount(rootfsHint.Mount) {
   934  			mountType = rootfsHint.Mount.Type
   935  		}
   936  	}
   937  	if c.Spec.Root.Readonly {
   938  		overlayMedium = config.NoOverlay
   939  	}
   940  	filestore, goferConf, err := c.createGoferFilestore(overlayMedium, c.Spec.Root.Path, mountType, false /* isShared */)
   941  	if err != nil {
   942  		return nil, nil, err
   943  	}
   944  	if filestore != nil {
   945  		goferFilestores = append(goferFilestores, filestore)
   946  	}
   947  	goferConfs = append(goferConfs, goferConf)
   948  
   949  	// Handle bind mounts.
   950  	for i := range c.Spec.Mounts {
   951  		if !specutils.IsGoferMount(c.Spec.Mounts[i]) {
   952  			continue
   953  		}
   954  		overlayMedium = ovlConf.SubMountOverlayMedium()
   955  		mountType = boot.Bind
   956  		isShared := false
   957  		if specutils.IsReadonlyMount(c.Spec.Mounts[i].Options) {
   958  			overlayMedium = config.NoOverlay
   959  		}
   960  		if hint := mountHints.FindMount(c.Spec.Mounts[i].Source); hint != nil {
   961  			// Note that we want overlayMedium=self even if this is a read-only mount so that
   962  			// the shared mount is created correctly. Future containers may mount this writably.
   963  			overlayMedium = config.SelfOverlay
   964  			if !specutils.IsGoferMount(hint.Mount) {
   965  				mountType = hint.Mount.Type
   966  			}
   967  			isShared = hint.ShouldShareMount()
   968  		}
   969  		filestore, goferConf, err := c.createGoferFilestore(overlayMedium, c.Spec.Mounts[i].Source, mountType, isShared)
   970  		if err != nil {
   971  			return nil, nil, err
   972  		}
   973  		if filestore != nil {
   974  			goferFilestores = append(goferFilestores, filestore)
   975  		}
   976  		goferConfs = append(goferConfs, goferConf)
   977  	}
   978  	for _, filestore := range goferFilestores {
   979  		// Perform this work around outside the sandbox. The sandbox may already be
   980  		// running with seccomp filters that do not allow this.
   981  		pgalloc.IMAWorkAroundForMemFile(filestore.Fd())
   982  	}
   983  	return goferFilestores, goferConfs, nil
   984  }
   985  
   986  func (c *Container) createGoferFilestore(overlayMedium config.OverlayMedium, mountSrc string, mountType string, isShared bool) (*os.File, boot.GoferMountConf, error) {
   987  	var lower boot.GoferMountConfLowerType
   988  	switch mountType {
   989  	case boot.Bind:
   990  		lower = boot.Lisafs
   991  	case tmpfs.Name:
   992  		lower = boot.NoneLower
   993  	case erofs.Name:
   994  		lower = boot.Erofs
   995  	default:
   996  		return nil, boot.GoferMountConf{}, fmt.Errorf("unsupported mount type %q in mount hint", mountType)
   997  	}
   998  	switch overlayMedium {
   999  	case config.NoOverlay:
  1000  		return nil, boot.GoferMountConf{Lower: lower, Upper: boot.NoOverlay}, nil
  1001  	case config.MemoryOverlay:
  1002  		return nil, boot.GoferMountConf{Lower: lower, Upper: boot.MemoryOverlay}, nil
  1003  	case config.SelfOverlay:
  1004  		return c.createGoferFilestoreInSelf(mountSrc, isShared, boot.GoferMountConf{Lower: lower, Upper: boot.SelfOverlay})
  1005  	default:
  1006  		if overlayMedium.IsBackedByAnon() {
  1007  			return c.createGoferFilestoreInDir(overlayMedium.HostFileDir(), boot.GoferMountConf{Lower: lower, Upper: boot.AnonOverlay})
  1008  		}
  1009  		return nil, boot.GoferMountConf{}, fmt.Errorf("unexpected overlay medium %q", overlayMedium)
  1010  	}
  1011  }
  1012  
  1013  func (c *Container) createGoferFilestoreInSelf(mountSrc string, isShared bool, successConf boot.GoferMountConf) (*os.File, boot.GoferMountConf, error) {
  1014  	mountSrcInfo, err := os.Stat(mountSrc)
  1015  	if err != nil {
  1016  		return nil, boot.GoferMountConf{}, fmt.Errorf("failed to stat mount %q to see if it were a directory: %v", mountSrc, err)
  1017  	}
  1018  	if !mountSrcInfo.IsDir() {
  1019  		log.Warningf("self filestore is only supported for directory mounts, but mount %q is not a directory, falling back to memory", mountSrc)
  1020  		return nil, boot.GoferMountConf{Lower: successConf.Lower, Upper: boot.MemoryOverlay}, nil
  1021  	}
  1022  	// Create the self filestore file.
  1023  	createFlags := unix.O_RDWR | unix.O_CREAT | unix.O_CLOEXEC
  1024  	if !isShared {
  1025  		// Allow shared mounts to reuse existing filestore. A previous shared user
  1026  		// may have already set up the filestore.
  1027  		createFlags |= unix.O_EXCL
  1028  	}
  1029  	filestorePath := boot.SelfFilestorePath(mountSrc, c.sandboxID())
  1030  	filestoreFD, err := unix.Open(filestorePath, createFlags, 0666)
  1031  	if err != nil {
  1032  		if err == unix.EEXIST {
  1033  			// Note that if the same submount is mounted multiple times within the
  1034  			// same sandbox, and is not shared, then the overlay option doesn't work
  1035  			// correctly. Because each overlay mount is independent and changes to
  1036  			// one are not visible to the other.
  1037  			return nil, boot.GoferMountConf{}, fmt.Errorf("%q mount source already has a filestore file at %q; repeated submounts are not supported with overlay optimizations", mountSrc, filestorePath)
  1038  		}
  1039  		return nil, boot.GoferMountConf{}, fmt.Errorf("failed to create filestore file inside %q: %v", mountSrc, err)
  1040  	}
  1041  	log.Debugf("Created filestore file at %q for mount source %q", filestorePath, mountSrc)
  1042  	// Filestore in self should be a named path because it needs to be
  1043  	// discoverable via path traversal so that k8s can scan the filesystem
  1044  	// and apply any limits appropriately (like local ephemeral storage
  1045  	// limits). So don't delete it. These files will be unlinked when the
  1046  	// container is destroyed. This makes self medium appropriate for k8s.
  1047  	return os.NewFile(uintptr(filestoreFD), filestorePath), successConf, nil
  1048  }
  1049  
  1050  func (c *Container) createGoferFilestoreInDir(filestoreDir string, successConf boot.GoferMountConf) (*os.File, boot.GoferMountConf, error) {
  1051  	fileInfo, err := os.Stat(filestoreDir)
  1052  	if err != nil {
  1053  		return nil, boot.GoferMountConf{}, fmt.Errorf("failed to stat filestore directory %q: %v", filestoreDir, err)
  1054  	}
  1055  	if !fileInfo.IsDir() {
  1056  		return nil, boot.GoferMountConf{}, fmt.Errorf("overlay2 flag should specify an existing directory")
  1057  	}
  1058  	// Create an unnamed temporary file in filestore directory which will be
  1059  	// deleted when the last FD on it is closed. We don't use O_TMPFILE because
  1060  	// it is not supported on all filesystems. So we simulate it by creating a
  1061  	// named file and then immediately unlinking it while keeping an FD on it.
  1062  	// This file will be deleted when the container exits.
  1063  	filestoreFile, err := os.CreateTemp(filestoreDir, "runsc-filestore-")
  1064  	if err != nil {
  1065  		return nil, boot.GoferMountConf{}, fmt.Errorf("failed to create a temporary file inside %q: %v", filestoreDir, err)
  1066  	}
  1067  	if err := unix.Unlink(filestoreFile.Name()); err != nil {
  1068  		return nil, boot.GoferMountConf{}, fmt.Errorf("failed to unlink temporary file %q: %v", filestoreFile.Name(), err)
  1069  	}
  1070  	log.Debugf("Created an unnamed filestore file at %q", filestoreDir)
  1071  	return filestoreFile, successConf, nil
  1072  }
  1073  
  1074  // saveLocked saves the container metadata to a file.
  1075  //
  1076  // Precondition: container must be locked with container.lock().
  1077  func (c *Container) saveLocked() error {
  1078  	log.Debugf("Save container, cid: %s", c.ID)
  1079  	if err := c.Saver.SaveLocked(c); err != nil {
  1080  		return fmt.Errorf("saving container metadata: %v", err)
  1081  	}
  1082  	return nil
  1083  }
  1084  
  1085  // stop stops the container (for regular containers) or the sandbox (for
  1086  // root containers), and waits for the container or sandbox and the gofer
  1087  // to stop. If any of them doesn't stop before timeout, an error is returned.
  1088  func (c *Container) stop() error {
  1089  	var parentCgroup cgroup.Cgroup
  1090  
  1091  	if c.Sandbox != nil {
  1092  		log.Debugf("Destroying container, cid: %s", c.ID)
  1093  		if err := c.Sandbox.DestroyContainer(c.ID); err != nil {
  1094  			return fmt.Errorf("destroying container %q: %v", c.ID, err)
  1095  		}
  1096  		// Only uninstall parentCgroup for sandbox stop.
  1097  		if c.Sandbox.IsRootContainer(c.ID) {
  1098  			parentCgroup = c.Sandbox.CgroupJSON.Cgroup
  1099  		}
  1100  		// Only set sandbox to nil after it has been told to destroy the container.
  1101  		c.Sandbox = nil
  1102  	}
  1103  
  1104  	// Try killing gofer if it does not exit with container.
  1105  	if c.GoferPid != 0 {
  1106  		log.Debugf("Killing gofer for container, cid: %s, PID: %d", c.ID, c.GoferPid)
  1107  		if err := unix.Kill(c.GoferPid, unix.SIGKILL); err != nil {
  1108  			// The gofer may already be stopped, log the error.
  1109  			log.Warningf("Error sending signal %d to gofer %d: %v", unix.SIGKILL, c.GoferPid, err)
  1110  		}
  1111  	}
  1112  
  1113  	if err := c.waitForStopped(); err != nil {
  1114  		return err
  1115  	}
  1116  
  1117  	// Delete container cgroup if any.
  1118  	if c.CompatCgroup.Cgroup != nil {
  1119  		if err := c.CompatCgroup.Cgroup.Uninstall(); err != nil {
  1120  			return err
  1121  		}
  1122  	}
  1123  	// Gofer is running inside parentCgroup, so Cgroup.Uninstall has to be called
  1124  	// after the gofer has stopped.
  1125  	if parentCgroup != nil {
  1126  		if err := parentCgroup.Uninstall(); err != nil {
  1127  			return err
  1128  		}
  1129  	}
  1130  	return nil
  1131  }
  1132  
  1133  func (c *Container) waitForStopped() error {
  1134  	if c.GoferPid == 0 {
  1135  		return nil
  1136  	}
  1137  
  1138  	if c.IsSandboxRunning() {
  1139  		if err := c.SignalContainer(unix.Signal(0), false); err == nil {
  1140  			return fmt.Errorf("container is still running")
  1141  		}
  1142  	}
  1143  
  1144  	if c.goferIsChild {
  1145  		// The gofer process is a child of the current process,
  1146  		// so we can wait it and collect its zombie.
  1147  		if _, err := unix.Wait4(int(c.GoferPid), nil, 0, nil); err != nil {
  1148  			return fmt.Errorf("error waiting the gofer process: %v", err)
  1149  		}
  1150  		c.GoferPid = 0
  1151  		return nil
  1152  	}
  1153  
  1154  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  1155  	defer cancel()
  1156  	b := backoff.WithContext(backoff.NewConstantBackOff(100*time.Millisecond), ctx)
  1157  	op := func() error {
  1158  		if err := unix.Kill(c.GoferPid, 0); err == nil {
  1159  			return fmt.Errorf("gofer is still running")
  1160  		}
  1161  		c.GoferPid = 0
  1162  		return nil
  1163  	}
  1164  	return backoff.Retry(op, b)
  1165  }
  1166  
  1167  // shouldCreateDeviceGofer indicates whether a device gofer connection should
  1168  // be created.
  1169  func shouldCreateDeviceGofer(spec *specs.Spec, conf *config.Config) bool {
  1170  	return specutils.GPUFunctionalityRequested(spec, conf) || specutils.TPUFunctionalityRequested(spec, conf)
  1171  }
  1172  
  1173  // shouldSpawnGofer indicates whether the gofer process should be spawned.
  1174  func shouldSpawnGofer(spec *specs.Spec, conf *config.Config, goferConfs []boot.GoferMountConf) bool {
  1175  	// Lisafs mounts need the gofer.
  1176  	for _, cfg := range goferConfs {
  1177  		if cfg.ShouldUseLisafs() {
  1178  			return true
  1179  		}
  1180  	}
  1181  	// Device gofer needs a gofer process.
  1182  	return shouldCreateDeviceGofer(spec, conf)
  1183  }
  1184  
  1185  // createGoferProcess returns an IO file list and a mounts file on success.
  1186  // The IO file list consists of image files and/or socket files to connect to
  1187  // a gofer endpoint for the mount points using Gofers. The mounts file is the
  1188  // file to read list of mounts after they have been resolved (direct paths,
  1189  // no symlinks), and will be nil if there is no cleaning required for mounts.
  1190  func (c *Container) createGoferProcess(spec *specs.Spec, conf *config.Config, bundleDir string, attached bool, rootfsHint *boot.RootfsHint) ([]*os.File, *os.File, *os.File, error) {
  1191  	if !shouldSpawnGofer(spec, conf, c.GoferMountConfs) {
  1192  		if !c.GoferMountConfs[0].ShouldUseErofs() {
  1193  			panic("goferless mode is only possible with EROFS rootfs")
  1194  		}
  1195  		ioFile, err := os.Open(rootfsHint.Mount.Source)
  1196  		if err != nil {
  1197  			return nil, nil, nil, fmt.Errorf("opening rootfs image %q: %v", rootfsHint.Mount.Source, err)
  1198  		}
  1199  		return []*os.File{ioFile}, nil, nil, nil
  1200  	}
  1201  
  1202  	donations := donation.Agency{}
  1203  	defer donations.Close()
  1204  
  1205  	if err := donations.OpenAndDonate("log-fd", conf.LogFilename, os.O_CREATE|os.O_WRONLY|os.O_APPEND); err != nil {
  1206  		return nil, nil, nil, err
  1207  	}
  1208  	if conf.DebugLog != "" {
  1209  		test := ""
  1210  		if len(conf.TestOnlyTestNameEnv) != 0 {
  1211  			// Fetch test name if one is provided and the test only flag was set.
  1212  			if t, ok := specutils.EnvVar(spec.Process.Env, conf.TestOnlyTestNameEnv); ok {
  1213  				test = t
  1214  			}
  1215  		}
  1216  		if specutils.IsDebugCommand(conf, "gofer") {
  1217  			if err := donations.DonateDebugLogFile("debug-log-fd", conf.DebugLog, "gofer", test); err != nil {
  1218  				return nil, nil, nil, err
  1219  			}
  1220  		}
  1221  	}
  1222  
  1223  	// Start with the general config flags.
  1224  	cmd := exec.Command(specutils.ExePath, conf.ToFlags()...)
  1225  	cmd.SysProcAttr = &unix.SysProcAttr{
  1226  		// Detach from session. Otherwise, signals sent to the foreground process
  1227  		// will also be forwarded by this process, resulting in duplicate signals.
  1228  		Setsid: true,
  1229  	}
  1230  
  1231  	// Set Args[0] to make easier to spot the gofer process. Otherwise it's
  1232  	// shown as `exe`.
  1233  	cmd.Args[0] = "runsc-gofer"
  1234  
  1235  	// Tranfer FDs that need to be present before the "gofer" command.
  1236  	// Start at 3 because 0, 1, and 2 are taken by stdin/out/err.
  1237  	nextFD := donations.Transfer(cmd, 3)
  1238  
  1239  	cmd.Args = append(cmd.Args, "gofer", "--bundle", bundleDir)
  1240  	cmd.Args = append(cmd.Args, "--gofer-mount-confs="+c.GoferMountConfs.String())
  1241  
  1242  	// Open the spec file to donate to the sandbox.
  1243  	specFile, err := specutils.OpenSpec(bundleDir)
  1244  	if err != nil {
  1245  		return nil, nil, nil, fmt.Errorf("opening spec file: %v", err)
  1246  	}
  1247  	donations.DonateAndClose("spec-fd", specFile)
  1248  
  1249  	// Donate any profile FDs to the gofer.
  1250  	if err := c.donateGoferProfileFDs(conf, &donations); err != nil {
  1251  		return nil, nil, nil, fmt.Errorf("donating gofer profile fds: %w", err)
  1252  	}
  1253  
  1254  	// Create pipe that allows gofer to send mount list to sandbox after all paths
  1255  	// have been resolved.
  1256  	mountsSand, mountsGofer, err := os.Pipe()
  1257  	if err != nil {
  1258  		return nil, nil, nil, err
  1259  	}
  1260  	donations.DonateAndClose("mounts-fd", mountsGofer)
  1261  
  1262  	// Count the number of mounts that needs an IO file.
  1263  	ioFileCount := 0
  1264  	for _, cfg := range c.GoferMountConfs {
  1265  		if cfg.ShouldUseLisafs() || cfg.ShouldUseErofs() {
  1266  			ioFileCount++
  1267  		}
  1268  	}
  1269  
  1270  	sandEnds := make([]*os.File, 0, ioFileCount)
  1271  	for i, cfg := range c.GoferMountConfs {
  1272  		switch {
  1273  		case cfg.ShouldUseLisafs():
  1274  			fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
  1275  			if err != nil {
  1276  				return nil, nil, nil, err
  1277  			}
  1278  			sandEnds = append(sandEnds, os.NewFile(uintptr(fds[0]), "sandbox IO FD"))
  1279  
  1280  			goferEnd := os.NewFile(uintptr(fds[1]), "gofer IO FD")
  1281  			donations.DonateAndClose("io-fds", goferEnd)
  1282  
  1283  		case cfg.ShouldUseErofs():
  1284  			if i > 0 {
  1285  				return nil, nil, nil, fmt.Errorf("EROFS lower layer is only supported for root mount")
  1286  			}
  1287  			f, err := os.Open(rootfsHint.Mount.Source)
  1288  			if err != nil {
  1289  				return nil, nil, nil, fmt.Errorf("opening rootfs image %q: %v", rootfsHint.Mount.Source, err)
  1290  			}
  1291  			sandEnds = append(sandEnds, f)
  1292  		}
  1293  	}
  1294  	var devSandEnd *os.File
  1295  	if shouldCreateDeviceGofer(spec, conf) {
  1296  		fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
  1297  		if err != nil {
  1298  			return nil, nil, nil, err
  1299  		}
  1300  		devSandEnd = os.NewFile(uintptr(fds[0]), "sandbox dev IO FD")
  1301  		donations.DonateAndClose("dev-io-fd", os.NewFile(uintptr(fds[1]), "gofer dev IO FD"))
  1302  	}
  1303  
  1304  	if attached {
  1305  		// The gofer is attached to the lifetime of this process, so it
  1306  		// should synchronously die when this process dies.
  1307  		cmd.SysProcAttr.Pdeathsig = unix.SIGKILL
  1308  	}
  1309  
  1310  	// Enter new namespaces to isolate from the rest of the system. Don't unshare
  1311  	// cgroup because gofer is added to a cgroup in the caller's namespace.
  1312  	nss := []specs.LinuxNamespace{
  1313  		{Type: specs.IPCNamespace},
  1314  		{Type: specs.MountNamespace},
  1315  		{Type: specs.NetworkNamespace},
  1316  		{Type: specs.PIDNamespace},
  1317  		{Type: specs.UTSNamespace},
  1318  	}
  1319  
  1320  	rootlessEUID := unix.Geteuid() != 0
  1321  	// Setup any uid/gid mappings, and create or join the configured user
  1322  	// namespace so the gofer's view of the filesystem aligns with the
  1323  	// users in the sandbox.
  1324  	if !rootlessEUID {
  1325  		if userNS, ok := specutils.GetNS(specs.UserNamespace, spec); ok {
  1326  			nss = append(nss, userNS)
  1327  			specutils.SetUIDGIDMappings(cmd, spec)
  1328  			// We need to set UID and GID to have capabilities in a new user namespace.
  1329  			cmd.SysProcAttr.Credential = &syscall.Credential{Uid: 0, Gid: 0}
  1330  		}
  1331  	} else {
  1332  		userNS, ok := specutils.GetNS(specs.UserNamespace, spec)
  1333  		if !ok {
  1334  			return nil, nil, nil, fmt.Errorf("unable to run a rootless container without userns")
  1335  		}
  1336  		nss = append(nss, userNS)
  1337  		syncFile, err := sandbox.ConfigureCmdForRootless(cmd, &donations)
  1338  		if err != nil {
  1339  			return nil, nil, nil, err
  1340  		}
  1341  		defer syncFile.Close()
  1342  	}
  1343  
  1344  	nvProxySetup, err := nvproxySetupAfterGoferUserns(spec, conf, cmd, &donations)
  1345  	if err != nil {
  1346  		return nil, nil, nil, fmt.Errorf("setting up nvproxy for gofer: %w", err)
  1347  	}
  1348  
  1349  	donations.Transfer(cmd, nextFD)
  1350  
  1351  	// Start the gofer in the given namespace.
  1352  	donation.LogDonations(cmd)
  1353  	log.Debugf("Starting gofer: %s %v", cmd.Path, cmd.Args)
  1354  	if err := specutils.StartInNS(cmd, nss); err != nil {
  1355  		return nil, nil, nil, fmt.Errorf("gofer: %v", err)
  1356  	}
  1357  	log.Infof("Gofer started, PID: %d", cmd.Process.Pid)
  1358  	c.GoferPid = cmd.Process.Pid
  1359  	c.goferIsChild = true
  1360  
  1361  	// Set up and synchronize rootless mode userns mappings.
  1362  	if rootlessEUID {
  1363  		if err := sandbox.SetUserMappings(spec, cmd.Process.Pid); err != nil {
  1364  			return nil, nil, nil, err
  1365  		}
  1366  	}
  1367  
  1368  	// Set up nvproxy within the Gofer namespace.
  1369  	if err := nvProxySetup(); err != nil {
  1370  		return nil, nil, nil, fmt.Errorf("nvproxy setup: %w", err)
  1371  	}
  1372  
  1373  	return sandEnds, devSandEnd, mountsSand, nil
  1374  }
  1375  
  1376  // changeStatus transitions from one status to another ensuring that the
  1377  // transition is valid.
  1378  func (c *Container) changeStatus(s Status) {
  1379  	switch s {
  1380  	case Creating:
  1381  		// Initial state, never transitions to it.
  1382  		panic(fmt.Sprintf("invalid state transition: %v => %v", c.Status, s))
  1383  
  1384  	case Created:
  1385  		if c.Status != Creating {
  1386  			panic(fmt.Sprintf("invalid state transition: %v => %v", c.Status, s))
  1387  		}
  1388  		if c.Sandbox == nil {
  1389  			panic("sandbox cannot be nil")
  1390  		}
  1391  
  1392  	case Paused:
  1393  		if c.Status != Running {
  1394  			panic(fmt.Sprintf("invalid state transition: %v => %v", c.Status, s))
  1395  		}
  1396  		if c.Sandbox == nil {
  1397  			panic("sandbox cannot be nil")
  1398  		}
  1399  
  1400  	case Running:
  1401  		if c.Status != Created && c.Status != Paused {
  1402  			panic(fmt.Sprintf("invalid state transition: %v => %v", c.Status, s))
  1403  		}
  1404  		if c.Sandbox == nil {
  1405  			panic("sandbox cannot be nil")
  1406  		}
  1407  
  1408  	case Stopped:
  1409  		if c.Status != Creating && c.Status != Created && c.Status != Running && c.Status != Stopped {
  1410  			panic(fmt.Sprintf("invalid state transition: %v => %v", c.Status, s))
  1411  		}
  1412  
  1413  	default:
  1414  		panic(fmt.Sprintf("invalid new state: %v", s))
  1415  	}
  1416  	c.Status = s
  1417  }
  1418  
  1419  // IsSandboxRunning returns true if the sandbox exists and is running.
  1420  func (c *Container) IsSandboxRunning() bool {
  1421  	return c.Sandbox != nil && c.Sandbox.IsRunning()
  1422  }
  1423  
  1424  // HasCapabilityInAnySet returns true if the given capability is in any of the
  1425  // capability sets of the container process.
  1426  func (c *Container) HasCapabilityInAnySet(capability linux.Capability) bool {
  1427  	capString := capability.String()
  1428  	for _, set := range [5][]string{
  1429  		c.Spec.Process.Capabilities.Bounding,
  1430  		c.Spec.Process.Capabilities.Effective,
  1431  		c.Spec.Process.Capabilities.Inheritable,
  1432  		c.Spec.Process.Capabilities.Permitted,
  1433  		c.Spec.Process.Capabilities.Ambient,
  1434  	} {
  1435  		for _, c := range set {
  1436  			if c == capString {
  1437  				return true
  1438  			}
  1439  		}
  1440  	}
  1441  	return false
  1442  }
  1443  
  1444  // RunsAsUID0 returns true if the container process runs with UID 0 (root).
  1445  func (c *Container) RunsAsUID0() bool {
  1446  	return c.Spec.Process.User.UID == 0
  1447  }
  1448  
  1449  func (c *Container) requireStatus(action string, statuses ...Status) error {
  1450  	for _, s := range statuses {
  1451  		if c.Status == s {
  1452  			return nil
  1453  		}
  1454  	}
  1455  	return fmt.Errorf("cannot %s container %q in state %s", action, c.ID, c.Status)
  1456  }
  1457  
  1458  // IsSandboxRoot returns true if this container is its sandbox's root container.
  1459  func (c *Container) IsSandboxRoot() bool {
  1460  	return isRoot(c.Spec)
  1461  }
  1462  
  1463  func isRoot(spec *specs.Spec) bool {
  1464  	return specutils.SpecContainerType(spec) != specutils.ContainerTypeContainer
  1465  }
  1466  
  1467  // runInCgroup executes fn inside the specified cgroup. If cg is nil, execute
  1468  // it in the current context.
  1469  func runInCgroup(cg cgroup.Cgroup, fn func() error) error {
  1470  	if cg == nil {
  1471  		return fn()
  1472  	}
  1473  	restore, err := cg.Join()
  1474  	if err != nil {
  1475  		return err
  1476  	}
  1477  	defer restore()
  1478  	return fn()
  1479  }
  1480  
  1481  // adjustGoferOOMScoreAdj sets the oom_store_adj for the container's gofer.
  1482  func (c *Container) adjustGoferOOMScoreAdj() error {
  1483  	if c.GoferPid == 0 || c.Spec.Process.OOMScoreAdj == nil {
  1484  		return nil
  1485  	}
  1486  	return setOOMScoreAdj(c.GoferPid, *c.Spec.Process.OOMScoreAdj)
  1487  }
  1488  
  1489  // adjustSandboxOOMScoreAdj sets the oom_score_adj for the sandbox.
  1490  // oom_score_adj is set to the lowest oom_score_adj among the containers
  1491  // running in the sandbox.
  1492  //
  1493  // TODO(gvisor.dev/issue/238): This call could race with other containers being
  1494  // created at the same time and end up setting the wrong oom_score_adj to the
  1495  // sandbox. Use rpc client to synchronize.
  1496  func adjustSandboxOOMScoreAdj(s *sandbox.Sandbox, spec *specs.Spec, rootDir string, destroy bool) error {
  1497  	// Adjustment can be skipped if the root container is exiting, because it
  1498  	// brings down the entire sandbox.
  1499  	if isRoot(spec) && destroy {
  1500  		return nil
  1501  	}
  1502  
  1503  	containers, err := LoadSandbox(rootDir, s.ID, LoadOpts{})
  1504  	if err != nil {
  1505  		return fmt.Errorf("loading sandbox containers: %v", err)
  1506  	}
  1507  
  1508  	// Do nothing if the sandbox has been terminated.
  1509  	if len(containers) == 0 {
  1510  		return nil
  1511  	}
  1512  
  1513  	// Get the lowest score for all containers.
  1514  	var lowScore int
  1515  	scoreFound := false
  1516  	for _, container := range containers {
  1517  		// Special multi-container support for CRI. Ignore the root container when
  1518  		// calculating oom_score_adj for the sandbox because it is the
  1519  		// infrastructure (pause) container and always has a very low oom_score_adj.
  1520  		//
  1521  		// We will use OOMScoreAdj in the single-container case where the
  1522  		// containerd container-type annotation is not present.
  1523  		if specutils.SpecContainerType(container.Spec) == specutils.ContainerTypeSandbox {
  1524  			continue
  1525  		}
  1526  
  1527  		if container.Spec.Process.OOMScoreAdj != nil && (!scoreFound || *container.Spec.Process.OOMScoreAdj < lowScore) {
  1528  			scoreFound = true
  1529  			lowScore = *container.Spec.Process.OOMScoreAdj
  1530  		}
  1531  	}
  1532  
  1533  	// If the container is destroyed and remaining containers have no
  1534  	// oomScoreAdj specified then we must revert to the original oom_score_adj
  1535  	// saved with the root container.
  1536  	if !scoreFound && destroy {
  1537  		lowScore = containers[0].Sandbox.OriginalOOMScoreAdj
  1538  		scoreFound = true
  1539  	}
  1540  
  1541  	// Only set oom_score_adj if one of the containers has oom_score_adj set. If
  1542  	// not, oom_score_adj is inherited from the parent process.
  1543  	//
  1544  	// See: https://github.com/opencontainers/runtime-spec/blob/master/config.md#linux-process
  1545  	if !scoreFound {
  1546  		return nil
  1547  	}
  1548  
  1549  	// Set the lowest of all containers oom_score_adj to the sandbox.
  1550  	return setOOMScoreAdj(s.Getpid(), lowScore)
  1551  }
  1552  
  1553  // setOOMScoreAdj sets oom_score_adj to the given value for the given PID.
  1554  // /proc must be available and mounted read-write. scoreAdj should be between
  1555  // -1000 and 1000. It's a noop if the process has already exited.
  1556  func setOOMScoreAdj(pid int, scoreAdj int) error {
  1557  	f, err := os.OpenFile(fmt.Sprintf("/proc/%d/oom_score_adj", pid), os.O_WRONLY, 0644)
  1558  	if err != nil {
  1559  		// Ignore NotExist errors because it can race with process exit.
  1560  		if os.IsNotExist(err) {
  1561  			log.Warningf("Process (%d) not found setting oom_score_adj", pid)
  1562  			return nil
  1563  		}
  1564  		return err
  1565  	}
  1566  	defer f.Close()
  1567  	if _, err := f.WriteString(strconv.Itoa(scoreAdj)); err != nil {
  1568  		if errors.Is(err, unix.ESRCH) {
  1569  			log.Warningf("Process (%d) exited while setting oom_score_adj", pid)
  1570  			return nil
  1571  		}
  1572  		return fmt.Errorf("setting oom_score_adj to %q: %v", scoreAdj, err)
  1573  	}
  1574  	return nil
  1575  }
  1576  
  1577  // populateStats populates event with stats estimates based on cgroups and the
  1578  // sentry's accounting.
  1579  func (c *Container) populateStats(event *boot.EventOut) {
  1580  	// The events command, when run for all running containers, should
  1581  	// account for the full cgroup CPU usage. We split cgroup usage
  1582  	// proportionally according to the sentry-internal usage measurements,
  1583  	// only counting Running containers.
  1584  	log.Debugf("event.ContainerUsage: %v", event.ContainerUsage)
  1585  	numContainers := uint64(len(event.ContainerUsage))
  1586  	if numContainers == 0 {
  1587  		log.Warningf("events: no containers listed in usage, returning zero CPU usage")
  1588  		event.Event.Data.CPU.Usage.Total = 0
  1589  		return
  1590  	}
  1591  
  1592  	var containerUsage uint64
  1593  	var allContainersUsage uint64
  1594  	for ID, usage := range event.ContainerUsage {
  1595  		allContainersUsage += usage
  1596  		if ID == c.ID {
  1597  			containerUsage = usage
  1598  		}
  1599  	}
  1600  
  1601  	cgroup, err := c.Sandbox.NewCGroup()
  1602  	if err != nil {
  1603  		// No cgroup, so rely purely on the sentry's accounting.
  1604  		log.Warningf("events: no cgroups")
  1605  		event.Event.Data.CPU.Usage.Total = containerUsage
  1606  		return
  1607  	}
  1608  
  1609  	// Get the host cgroup CPU usage.
  1610  	cgroupsUsage, err := cgroup.CPUUsage()
  1611  	if err != nil || cgroupsUsage == 0 {
  1612  		// No cgroup usage, so rely purely on the sentry's accounting.
  1613  		log.Warningf("events: failed when getting cgroup CPU usage for container: usage=%d, err: %v", cgroupsUsage, err)
  1614  		event.Event.Data.CPU.Usage.Total = containerUsage
  1615  		return
  1616  	}
  1617  
  1618  	// If the sentry reports no CPU usage, fall back on cgroups and split usage
  1619  	// equally across containers.
  1620  	if allContainersUsage == 0 {
  1621  		log.Warningf("events: no sentry CPU usage reported")
  1622  		allContainersUsage = cgroupsUsage
  1623  		containerUsage = cgroupsUsage / numContainers
  1624  	}
  1625  
  1626  	// Scaling can easily overflow a uint64 (e.g. a containerUsage and
  1627  	// cgroupsUsage of 16 seconds each will overflow), so use floats.
  1628  	total := float64(containerUsage) * (float64(cgroupsUsage) / float64(allContainersUsage))
  1629  	log.Debugf("Usage, container: %d, cgroups: %d, all: %d, total: %.0f", containerUsage, cgroupsUsage, allContainersUsage, total)
  1630  	event.Event.Data.CPU.Usage.Total = uint64(total)
  1631  	return
  1632  }
  1633  
  1634  // setupCgroupForRoot configures and returns cgroup for the sandbox and the
  1635  // root container. If `cgroupParentAnnotation` is set, use that path as the
  1636  // sandbox cgroup and use Spec.Linux.CgroupsPath as the root container cgroup.
  1637  func (c *Container) setupCgroupForRoot(conf *config.Config, spec *specs.Spec) (cgroup.Cgroup, cgroup.Cgroup, error) {
  1638  	var parentCgroup cgroup.Cgroup
  1639  	if parentPath, ok := spec.Annotations[cgroupParentAnnotation]; ok {
  1640  		var err error
  1641  		parentCgroup, err = cgroup.NewFromPath(parentPath, conf.SystemdCgroup)
  1642  		if err != nil {
  1643  			return nil, nil, err
  1644  		}
  1645  	} else {
  1646  		var err error
  1647  		parentCgroup, err = cgroup.NewFromSpec(spec, conf.SystemdCgroup)
  1648  		if parentCgroup == nil || err != nil {
  1649  			return nil, nil, err
  1650  		}
  1651  	}
  1652  
  1653  	var err error
  1654  	parentCgroup, err = cgroupInstall(conf, parentCgroup, spec.Linux.Resources)
  1655  	if parentCgroup == nil || err != nil {
  1656  		return nil, nil, err
  1657  	}
  1658  
  1659  	subCgroup, err := c.setupCgroupForSubcontainer(conf, spec)
  1660  	if err != nil {
  1661  		_ = parentCgroup.Uninstall()
  1662  		return nil, nil, err
  1663  	}
  1664  	return parentCgroup, subCgroup, nil
  1665  }
  1666  
  1667  // setupCgroupForSubcontainer sets up empty cgroups for subcontainers. Since
  1668  // subcontainers run exclusively inside the sandbox, subcontainer cgroups on the
  1669  // host have no effect on them. However, some tools (e.g. cAdvisor) uses cgroups
  1670  // paths to discover new containers and report stats for them.
  1671  func (c *Container) setupCgroupForSubcontainer(conf *config.Config, spec *specs.Spec) (cgroup.Cgroup, error) {
  1672  	if isRoot(spec) {
  1673  		if _, ok := spec.Annotations[cgroupParentAnnotation]; !ok {
  1674  			return nil, nil
  1675  		}
  1676  	}
  1677  
  1678  	cg, err := cgroup.NewFromSpec(spec, conf.SystemdCgroup)
  1679  	if cg == nil || err != nil {
  1680  		return nil, err
  1681  	}
  1682  	// Use empty resources, just want the directory structure created.
  1683  	return cgroupInstall(conf, cg, &specs.LinuxResources{})
  1684  }
  1685  
  1686  // donateGoferProfileFDs will open profile files and donate their FDs to the
  1687  // gofer.
  1688  func (c *Container) donateGoferProfileFDs(conf *config.Config, donations *donation.Agency) error {
  1689  	// The gofer profile files are named based on the provided flag, but
  1690  	// suffixed with "gofer" and the container ID to avoid collisions with
  1691  	// sentry profile files or profile files from other gofers.
  1692  	//
  1693  	// TODO(b/243183772): Merge gofer profile data with sentry profile data
  1694  	// into a single file.
  1695  	profSuffix := ".gofer." + c.ID
  1696  	const profFlags = os.O_CREATE | os.O_WRONLY | os.O_TRUNC
  1697  	if conf.ProfileBlock != "" {
  1698  		if err := donations.OpenAndDonate("profile-block-fd", conf.ProfileBlock+profSuffix, profFlags); err != nil {
  1699  			return err
  1700  		}
  1701  	}
  1702  	if conf.ProfileCPU != "" {
  1703  		if err := donations.OpenAndDonate("profile-cpu-fd", conf.ProfileCPU+profSuffix, profFlags); err != nil {
  1704  			return err
  1705  		}
  1706  	}
  1707  	if conf.ProfileHeap != "" {
  1708  		if err := donations.OpenAndDonate("profile-heap-fd", conf.ProfileHeap+profSuffix, profFlags); err != nil {
  1709  			return err
  1710  		}
  1711  	}
  1712  	if conf.ProfileMutex != "" {
  1713  		if err := donations.OpenAndDonate("profile-mutex-fd", conf.ProfileMutex+profSuffix, profFlags); err != nil {
  1714  			return err
  1715  		}
  1716  	}
  1717  	if conf.TraceFile != "" {
  1718  		if err := donations.OpenAndDonate("trace-fd", conf.TraceFile+profSuffix, profFlags); err != nil {
  1719  			return err
  1720  		}
  1721  	}
  1722  	return nil
  1723  }
  1724  
  1725  // cgroupInstall creates cgroups dir structure and sets their respective
  1726  // resources. In case of success, returns the cgroups instance and nil error.
  1727  // For rootless, it's possible that cgroups operations fail, in this case the
  1728  // error is suppressed and a nil cgroups instance is returned to indicate that
  1729  // no cgroups was configured.
  1730  func cgroupInstall(conf *config.Config, cg cgroup.Cgroup, res *specs.LinuxResources) (cgroup.Cgroup, error) {
  1731  	if err := cg.Install(res); err != nil {
  1732  		switch {
  1733  		case (errors.Is(err, unix.EACCES) || errors.Is(err, unix.EROFS)) && conf.Rootless:
  1734  			log.Warningf("Skipping cgroup configuration in rootless mode: %v", err)
  1735  			return nil, nil
  1736  		default:
  1737  			return nil, fmt.Errorf("configuring cgroup: %v", err)
  1738  		}
  1739  	}
  1740  	return cg, nil
  1741  }
  1742  
  1743  func modifySpecForDirectfs(conf *config.Config, spec *specs.Spec) error {
  1744  	if !conf.DirectFS || conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
  1745  		return nil
  1746  	}
  1747  	if conf.Network == config.NetworkHost {
  1748  		// Hostnet feature requires the sandbox to run in the current user
  1749  		// namespace, in which the network namespace is configured.
  1750  		return nil
  1751  	}
  1752  	if _, ok := specutils.GetNS(specs.UserNamespace, spec); ok {
  1753  		// If the spec already defines a userns, use that.
  1754  		return nil
  1755  	}
  1756  	if spec.Linux == nil {
  1757  		spec.Linux = &specs.Linux{}
  1758  	}
  1759  	if len(spec.Linux.UIDMappings) > 0 || len(spec.Linux.GIDMappings) > 0 {
  1760  		// The spec can only define UID/GID mappings with a userns (checked above).
  1761  		return fmt.Errorf("spec defines UID/GID mappings without defining userns")
  1762  	}
  1763  	// Run the sandbox in a new user namespace with identity UID/GID mappings.
  1764  	log.Debugf("Configuring container with a new userns with identity user mappings into current userns")
  1765  	spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{Type: specs.UserNamespace})
  1766  	uidMappings, err := getIdentityMapping("uid_map")
  1767  	if err != nil {
  1768  		return err
  1769  	}
  1770  	spec.Linux.UIDMappings = uidMappings
  1771  	logIDMappings(uidMappings, "UID")
  1772  	gidMappings, err := getIdentityMapping("gid_map")
  1773  	if err != nil {
  1774  		return err
  1775  	}
  1776  	spec.Linux.GIDMappings = gidMappings
  1777  	logIDMappings(gidMappings, "GID")
  1778  	return nil
  1779  }
  1780  
  1781  func getIdentityMapping(mapFileName string) ([]specs.LinuxIDMapping, error) {
  1782  	// See user_namespaces(7) to understand how /proc/self/{uid/gid}_map files
  1783  	// are organized.
  1784  	mapFile := path.Join("/proc/self", mapFileName)
  1785  	file, err := os.Open(mapFile)
  1786  	if err != nil {
  1787  		return nil, fmt.Errorf("failed to open %s: %v", mapFile, err)
  1788  	}
  1789  	defer file.Close()
  1790  
  1791  	var mappings []specs.LinuxIDMapping
  1792  	scanner := bufio.NewScanner(file)
  1793  	for scanner.Scan() {
  1794  		line := scanner.Text()
  1795  		var myStart, parentStart, rangeLen uint32
  1796  		numParsed, err := fmt.Sscanf(line, "%d %d %d", &myStart, &parentStart, &rangeLen)
  1797  		if err != nil {
  1798  			return nil, fmt.Errorf("failed to parse line %q in file %s: %v", line, mapFile, err)
  1799  		}
  1800  		if numParsed != 3 {
  1801  			return nil, fmt.Errorf("failed to parse 3 integers from line %q in file %s", line, mapFile)
  1802  		}
  1803  		// Create an identity mapping with the current userns.
  1804  		mappings = append(mappings, specs.LinuxIDMapping{
  1805  			ContainerID: myStart,
  1806  			HostID:      myStart,
  1807  			Size:        rangeLen,
  1808  		})
  1809  	}
  1810  	if err := scanner.Err(); err != nil {
  1811  		return nil, fmt.Errorf("failed to scan file %s: %v", mapFile, err)
  1812  	}
  1813  	return mappings, nil
  1814  }
  1815  
  1816  func logIDMappings(mappings []specs.LinuxIDMapping, idType string) {
  1817  	if !log.IsLogging(log.Debug) {
  1818  		return
  1819  	}
  1820  	log.Debugf("%s Mappings:", idType)
  1821  	for _, m := range mappings {
  1822  		log.Debugf("\tContainer ID: %d, Host ID: %d, Range Length: %d", m.ContainerID, m.HostID, m.Size)
  1823  	}
  1824  }
  1825  
  1826  // nvProxyPreGoferHostSetup does host setup work so that `nvidia-container-cli
  1827  // configure` can be run in the future. It runs before any Gofers start.
  1828  // It verifies that all the required dependencies are in place, loads kernel
  1829  // modules, and ensures the correct device files exist and are accessible.
  1830  // This should only be necessary once on the host. It should be run during the
  1831  // root container setup sequence to make sure it has run at least once.
  1832  func nvProxyPreGoferHostSetup(spec *specs.Spec, conf *config.Config) error {
  1833  	if !specutils.GPUFunctionalityRequestedViaHook(spec, conf) {
  1834  		return nil
  1835  	}
  1836  
  1837  	// Locate binaries. For security reasons, unlike
  1838  	// nvidia-container-runtime-hook, we don't add the container's filesystem
  1839  	// to the search path. We also don't support
  1840  	// /etc/nvidia-container-runtime/config.toml to avoid importing a TOML
  1841  	// parser.
  1842  	cliPath, err := exec.LookPath("nvidia-container-cli")
  1843  	if err != nil {
  1844  		return fmt.Errorf("failed to locate nvidia-container-cli in PATH: %w", err)
  1845  	}
  1846  
  1847  	// nvidia-container-cli --load-kmods seems to be a noop; load kernel modules ourselves.
  1848  	nvproxyLoadKernelModules()
  1849  
  1850  	if _, err := os.Stat("/dev/nvidiactl"); err != nil {
  1851  		if !os.IsNotExist(err) {
  1852  			return fmt.Errorf("stat(2) for /dev/nvidiactl failed: %w", err)
  1853  		}
  1854  
  1855  		// Run `nvidia-container-cli info`.
  1856  		// This has the side-effect of automatically creating GPU device files.
  1857  		argv := []string{cliPath, "--load-kmods", "info"}
  1858  		log.Debugf("Executing %q", argv)
  1859  		var infoOut, infoErr strings.Builder
  1860  		cmd := exec.Cmd{
  1861  			Path:   argv[0],
  1862  			Args:   argv,
  1863  			Env:    os.Environ(),
  1864  			Stdout: &infoOut,
  1865  			Stderr: &infoErr,
  1866  		}
  1867  		if err := cmd.Run(); err != nil {
  1868  			return fmt.Errorf("nvidia-container-cli info failed, err: %v\nstdout: %s\nstderr: %s", err, infoOut.String(), infoErr.String())
  1869  		}
  1870  		log.Debugf("nvidia-container-cli info: %v", infoOut.String())
  1871  	}
  1872  
  1873  	return nil
  1874  }
  1875  
  1876  // nvproxyLoadKernelModules loads NVIDIA-related kernel modules with modprobe.
  1877  func nvproxyLoadKernelModules() {
  1878  	for _, mod := range [...]string{
  1879  		"nvidia",
  1880  		"nvidia-uvm",
  1881  	} {
  1882  		argv := []string{
  1883  			"/sbin/modprobe",
  1884  			mod,
  1885  		}
  1886  		log.Debugf("Executing %q", argv)
  1887  		var stdout, stderr strings.Builder
  1888  		cmd := exec.Cmd{
  1889  			Path:   argv[0],
  1890  			Args:   argv,
  1891  			Env:    os.Environ(),
  1892  			Stdout: &stdout,
  1893  			Stderr: &stderr,
  1894  		}
  1895  		if err := cmd.Run(); err != nil {
  1896  			// This might not be fatal since modules may already be loaded. Log
  1897  			// the failure but continue.
  1898  			log.Warningf("modprobe %s failed, err: %v\nstdout: %s\nstderr: %s", mod, err, stdout.String(), stderr.String())
  1899  		}
  1900  	}
  1901  }
  1902  
  1903  // nvproxySetupAfterGoferUserns runs `nvidia-container-cli configure`.
  1904  // This sets up the container filesystem with bind mounts that allow it to
  1905  // use NVIDIA devices.
  1906  //
  1907  // This should be called during the Gofer setup process, as the bind mounts
  1908  // are created in the Gofer's mount namespace.
  1909  // If successful, it returns a callback function that must be called once the
  1910  // Gofer process has started.
  1911  // This function has no effect if nvproxy functionality is not requested.
  1912  //
  1913  // This function essentially replicates
  1914  // nvidia-container-toolkit:cmd/nvidia-container-runtime-hook, i.e. the
  1915  // binary that executeHook() is hard-coded to skip, with differences noted
  1916  // inline. We do this rather than move the prestart hook because the
  1917  // "runtime environment" in which prestart hooks execute is vaguely
  1918  // defined, such that nvidia-container-runtime-hook and existing runsc
  1919  // hooks differ in their expected environment.
  1920  //
  1921  // Note that nvidia-container-cli will set up files in /dev and /proc which
  1922  // are useless, since they will be hidden by sentry devtmpfs and procfs
  1923  // respectively (and some device files will have the wrong device numbers
  1924  // from the application's perspective since nvproxy may register device
  1925  // numbers in sentry VFS that differ from those on the host, e.g. for
  1926  // nvidia-uvm). These files are separately created during sandbox VFS
  1927  // construction. For this reason, we don't need to parse
  1928  // NVIDIA_VISIBLE_DEVICES or pass --device to nvidia-container-cli.
  1929  func nvproxySetupAfterGoferUserns(spec *specs.Spec, conf *config.Config, goferCmd *exec.Cmd, goferDonations *donation.Agency) (func() error, error) {
  1930  	if !specutils.GPUFunctionalityRequestedViaHook(spec, conf) {
  1931  		return func() error { return nil }, nil
  1932  	}
  1933  
  1934  	if spec.Root == nil {
  1935  		return nil, fmt.Errorf("spec missing root filesystem")
  1936  	}
  1937  
  1938  	// nvidia-container-cli does not create this directory.
  1939  	if err := os.MkdirAll(path.Join(spec.Root.Path, "proc", "driver", "nvidia"), 0555); err != nil {
  1940  		return nil, fmt.Errorf("failed to create /proc/driver/nvidia in app filesystem: %w", err)
  1941  	}
  1942  
  1943  	cliPath, err := exec.LookPath("nvidia-container-cli")
  1944  	if err != nil {
  1945  		return nil, fmt.Errorf("failed to locate nvidia-container-cli in PATH: %w", err)
  1946  	}
  1947  
  1948  	// On Ubuntu, ldconfig is a wrapper around ldconfig.real, and we need the latter.
  1949  	var ldconfigPath string
  1950  	if _, err := os.Stat("/sbin/ldconfig.real"); err == nil {
  1951  		ldconfigPath = "/sbin/ldconfig.real"
  1952  	} else {
  1953  		ldconfigPath = "/sbin/ldconfig"
  1954  	}
  1955  
  1956  	devices, err := specutils.ParseNvidiaVisibleDevices(spec)
  1957  	if err != nil {
  1958  		return nil, fmt.Errorf("failed to get nvidia device numbers: %w", err)
  1959  	}
  1960  
  1961  	// Create synchronization FD for nvproxy.
  1962  	fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
  1963  	if err != nil {
  1964  		return nil, err
  1965  	}
  1966  	ourEnd := os.NewFile(uintptr(fds[0]), "nvproxy sync runsc FD")
  1967  	goferEnd := os.NewFile(uintptr(fds[1]), "nvproxy sync gofer FD")
  1968  	goferDonations.DonateAndClose("sync-nvproxy-fd", goferEnd)
  1969  
  1970  	return func() error {
  1971  		defer ourEnd.Close()
  1972  		argv := []string{
  1973  			cliPath,
  1974  			"--load-kmods",
  1975  			"configure",
  1976  			fmt.Sprintf("--ldconfig=@%s", ldconfigPath),
  1977  			"--no-cgroups", // runsc doesn't configure device cgroups yet
  1978  			"--utility",
  1979  			"--compute",
  1980  			fmt.Sprintf("--pid=%d", goferCmd.Process.Pid),
  1981  			fmt.Sprintf("--device=%s", devices),
  1982  			spec.Root.Path,
  1983  		}
  1984  		log.Debugf("Executing %q", argv)
  1985  		var stdout, stderr strings.Builder
  1986  		cmd := exec.Cmd{
  1987  			Path:   argv[0],
  1988  			Args:   argv,
  1989  			Env:    os.Environ(),
  1990  			Stdout: &stdout,
  1991  			Stderr: &stderr,
  1992  		}
  1993  		if err := cmd.Run(); err != nil {
  1994  			return fmt.Errorf("nvidia-container-cli configure failed, err: %v\nstdout: %s\nstderr: %s", err, stdout.String(), stderr.String())
  1995  		}
  1996  		return nil
  1997  	}, nil
  1998  }