github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/runsc/cmd/run.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 cmd
    16  
    17  import (
    18  	"context"
    19  	"os"
    20  
    21  	"github.com/google/subcommands"
    22  	"golang.org/x/sys/unix"
    23  	"github.com/metacubex/gvisor/pkg/log"
    24  	"github.com/metacubex/gvisor/runsc/cmd/util"
    25  	"github.com/metacubex/gvisor/runsc/config"
    26  	"github.com/metacubex/gvisor/runsc/container"
    27  	"github.com/metacubex/gvisor/runsc/flag"
    28  	"github.com/metacubex/gvisor/runsc/specutils"
    29  )
    30  
    31  // Run implements subcommands.Command for the "run" command.
    32  type Run struct {
    33  	// Run flags are a super-set of those for Create.
    34  	Create
    35  
    36  	// detach indicates that runsc has to start a process and exit without waiting it.
    37  	detach bool
    38  
    39  	// passFDs are user-supplied FDs from the host to be exposed to the
    40  	// sandboxed app.
    41  	passFDs fdMappings
    42  
    43  	// execFD is the host file descriptor used for program execution.
    44  	execFD int
    45  }
    46  
    47  // Name implements subcommands.Command.Name.
    48  func (*Run) Name() string {
    49  	return "run"
    50  }
    51  
    52  // Synopsis implements subcommands.Command.Synopsis.
    53  func (*Run) Synopsis() string {
    54  	return "create and run a secure container"
    55  }
    56  
    57  // Usage implements subcommands.Command.Usage.
    58  func (*Run) Usage() string {
    59  	return `run [flags] <container id> - create and run a secure container.
    60  `
    61  }
    62  
    63  // SetFlags implements subcommands.Command.SetFlags.
    64  func (r *Run) SetFlags(f *flag.FlagSet) {
    65  	f.BoolVar(&r.detach, "detach", false, "detach from the container's process")
    66  	f.Var(&r.passFDs, "pass-fd", "file descriptor passed to the container in M:N format, where M is the host and N is the guest descriptor (can be supplied multiple times)")
    67  	f.IntVar(&r.execFD, "exec-fd", -1, "host file descriptor used for program execution")
    68  	r.Create.SetFlags(f)
    69  }
    70  
    71  // Execute implements subcommands.Command.Execute.
    72  func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
    73  	if f.NArg() != 1 {
    74  		f.Usage()
    75  		return subcommands.ExitUsageError
    76  	}
    77  
    78  	id := f.Arg(0)
    79  	conf := args[0].(*config.Config)
    80  	waitStatus := args[1].(*unix.WaitStatus)
    81  
    82  	if conf.Rootless {
    83  		if conf.Network == config.NetworkSandbox {
    84  			return util.Errorf("sandbox network isn't supported with --rootless, use --network=none or --network=host")
    85  		}
    86  
    87  		if err := specutils.MaybeRunAsRoot(); err != nil {
    88  			return util.Errorf("Error executing inside namespace: %v", err)
    89  		}
    90  		// Execution will continue here if no more capabilities are needed...
    91  	}
    92  
    93  	bundleDir := r.bundleDir
    94  	if bundleDir == "" {
    95  		bundleDir = getwdOrDie()
    96  	}
    97  	spec, err := specutils.ReadSpec(bundleDir, conf)
    98  	if err != nil {
    99  		return util.Errorf("reading spec: %v", err)
   100  	}
   101  	specutils.LogSpecDebug(spec, conf.OCISeccomp)
   102  
   103  	// Create files from file descriptors.
   104  	fdMap := make(map[int]*os.File)
   105  	for _, mapping := range r.passFDs {
   106  		file := os.NewFile(uintptr(mapping.Host), "")
   107  		if file == nil {
   108  			return util.Errorf("Failed to create file from file descriptor %d", mapping.Host)
   109  		}
   110  		fdMap[mapping.Guest] = file
   111  	}
   112  
   113  	var execFile *os.File
   114  	if r.execFD >= 0 {
   115  		execFile = os.NewFile(uintptr(r.execFD), "exec-fd")
   116  	}
   117  
   118  	// Close the underlying file descriptors after we have passed them.
   119  	defer func() {
   120  		for _, file := range fdMap {
   121  			fd := file.Fd()
   122  			if file.Close() != nil {
   123  				log.Debugf("Failed to close FD %d", fd)
   124  			}
   125  		}
   126  
   127  		if execFile != nil && execFile.Close() != nil {
   128  			log.Debugf("Failed to close exec FD")
   129  		}
   130  	}()
   131  
   132  	runArgs := container.Args{
   133  		ID:            id,
   134  		Spec:          spec,
   135  		BundleDir:     bundleDir,
   136  		ConsoleSocket: r.consoleSocket,
   137  		PIDFile:       r.pidFile,
   138  		UserLog:       r.userLog,
   139  		Attached:      !r.detach,
   140  		PassFiles:     fdMap,
   141  		ExecFile:      execFile,
   142  	}
   143  	ws, err := container.Run(conf, runArgs)
   144  	if err != nil {
   145  		return util.Errorf("running container: %v", err)
   146  	}
   147  
   148  	*waitStatus = ws
   149  	return subcommands.ExitSuccess
   150  }