gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "gvisor.dev/gvisor/pkg/log" 24 "gvisor.dev/gvisor/runsc/cmd/util" 25 "gvisor.dev/gvisor/runsc/config" 26 "gvisor.dev/gvisor/runsc/container" 27 "gvisor.dev/gvisor/runsc/flag" 28 "gvisor.dev/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 }