gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/cmd/restore.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/cleanup" 24 "gvisor.dev/gvisor/pkg/log" 25 "gvisor.dev/gvisor/runsc/cmd/util" 26 "gvisor.dev/gvisor/runsc/config" 27 "gvisor.dev/gvisor/runsc/container" 28 "gvisor.dev/gvisor/runsc/flag" 29 "gvisor.dev/gvisor/runsc/specutils" 30 ) 31 32 // Restore implements subcommands.Command for the "restore" command. 33 type Restore struct { 34 // Restore flags are a super-set of those for Create. 35 Create 36 37 // imagePath is the path to the saved container image 38 imagePath string 39 40 // detach indicates that runsc has to start a process and exit without waiting it. 41 detach bool 42 43 // direct indicates whether O_DIRECT should be used for reading the 44 // checkpoint pages file. It is faster if the checkpoint files are not 45 // already in the page cache (for example if its coming from an untouched 46 // network block device). Usually the restore is done only once, so the cost 47 // of adding the checkpoint files to the page cache can be redundant. 48 direct bool 49 } 50 51 // Name implements subcommands.Command.Name. 52 func (*Restore) Name() string { 53 return "restore" 54 } 55 56 // Synopsis implements subcommands.Command.Synopsis. 57 func (*Restore) Synopsis() string { 58 return "restore a saved state of container (experimental)" 59 } 60 61 // Usage implements subcommands.Command.Usage. 62 func (*Restore) Usage() string { 63 return `restore [flags] <container id> - restore saved state of container. 64 ` 65 } 66 67 // SetFlags implements subcommands.Command.SetFlags. 68 func (r *Restore) SetFlags(f *flag.FlagSet) { 69 r.Create.SetFlags(f) 70 f.StringVar(&r.imagePath, "image-path", "", "directory path to saved container image") 71 f.BoolVar(&r.detach, "detach", false, "detach from the container's process") 72 f.BoolVar(&r.direct, "direct", false, "use O_DIRECT for reading checkpoint pages file") 73 74 // Unimplemented flags necessary for compatibility with docker. 75 76 var nsr bool 77 f.BoolVar(&nsr, "no-subreaper", false, "ignored") 78 79 var wp string 80 f.StringVar(&wp, "work-path", "", "ignored") 81 } 82 83 // Execute implements subcommands.Command.Execute. 84 func (r *Restore) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus { 85 if f.NArg() != 1 { 86 f.Usage() 87 return subcommands.ExitUsageError 88 } 89 90 id := f.Arg(0) 91 conf := args[0].(*config.Config) 92 waitStatus := args[1].(*unix.WaitStatus) 93 94 if conf.Rootless { 95 return util.Errorf("Rootless mode not supported with %q", r.Name()) 96 } 97 98 bundleDir := r.bundleDir 99 if bundleDir == "" { 100 bundleDir = getwdOrDie() 101 } 102 if r.imagePath == "" { 103 return util.Errorf("image-path flag must be provided") 104 } 105 106 var cu cleanup.Cleanup 107 defer cu.Clean() 108 109 runArgs := container.Args{ 110 ID: id, 111 Spec: nil, 112 BundleDir: bundleDir, 113 ConsoleSocket: r.consoleSocket, 114 PIDFile: r.pidFile, 115 UserLog: r.userLog, 116 Attached: !r.detach, 117 } 118 119 log.Debugf("Restore container, cid: %s, rootDir: %q", id, conf.RootDir) 120 c, err := container.Load(conf.RootDir, container.FullID{ContainerID: id}, container.LoadOpts{}) 121 if err != nil { 122 if err != os.ErrNotExist { 123 return util.Errorf("loading container: %v", err) 124 } 125 126 log.Warningf("Container not found, creating new one, cid: %s, spec from: %s", id, bundleDir) 127 128 // Read the spec again here to ensure flag annotations from the spec are 129 // applied to "conf". 130 if runArgs.Spec, err = specutils.ReadSpec(bundleDir, conf); err != nil { 131 return util.Errorf("reading spec: %v", err) 132 } 133 specutils.LogSpecDebug(runArgs.Spec, conf.OCISeccomp) 134 135 if c, err = container.New(conf, runArgs); err != nil { 136 return util.Errorf("creating container: %v", err) 137 } 138 139 // Clean up partially created container if an error occurs. 140 // Any errors returned by Destroy() itself are ignored. 141 cu.Add(func() { 142 c.Destroy() 143 }) 144 } else { 145 runArgs.Spec = c.Spec 146 } 147 148 log.Debugf("Restore: %v", r.imagePath) 149 if err := c.Restore(conf, r.imagePath, r.direct); err != nil { 150 return util.Errorf("starting container: %v", err) 151 } 152 153 // If we allocate a terminal, forward signals to the sandbox process. 154 // Otherwise, Ctrl+C will terminate this process and its children, 155 // including the terminal. 156 if c.Spec.Process.Terminal { 157 stopForwarding := c.ForwardSignals(0, true /* fgProcess */) 158 defer stopForwarding() 159 } 160 161 var ws unix.WaitStatus 162 if runArgs.Attached { 163 if ws, err = c.Wait(); err != nil { 164 return util.Errorf("running container: %v", err) 165 } 166 } 167 *waitStatus = ws 168 169 cu.Release() 170 171 return subcommands.ExitSuccess 172 }