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