github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/utils_linux.go (about) 1 // +build linux 2 3 package main 4 5 import ( 6 "errors" 7 "fmt" 8 "os" 9 "path/filepath" 10 "strconv" 11 "syscall" 12 13 "github.com/Sirupsen/logrus" 14 "github.com/coreos/go-systemd/activation" 15 "github.com/opencontainers/runc/libcontainer" 16 "github.com/opencontainers/runc/libcontainer/cgroups/systemd" 17 "github.com/opencontainers/runc/libcontainer/specconv" 18 "github.com/opencontainers/runtime-spec/specs-go" 19 "github.com/urfave/cli" 20 ) 21 22 var errEmptyID = errors.New("container id cannot be empty") 23 24 var container libcontainer.Container 25 26 // loadFactory returns the configured factory instance for execing containers. 27 func loadFactory(context *cli.Context) (libcontainer.Factory, error) { 28 root := context.GlobalString("root") 29 abs, err := filepath.Abs(root) 30 if err != nil { 31 return nil, err 32 } 33 cgroupManager := libcontainer.Cgroupfs 34 if context.GlobalBool("systemd-cgroup") { 35 if systemd.UseSystemd() { 36 cgroupManager = libcontainer.SystemdCgroups 37 } else { 38 return nil, fmt.Errorf("systemd cgroup flag passed, but systemd support for managing cgroups is not available") 39 } 40 } 41 return libcontainer.New(abs, cgroupManager, libcontainer.CriuPath(context.GlobalString("criu"))) 42 } 43 44 // getContainer returns the specified container instance by loading it from state 45 // with the default factory. 46 func getContainer(context *cli.Context) (libcontainer.Container, error) { 47 id := context.Args().First() 48 if id == "" { 49 return nil, errEmptyID 50 } 51 factory, err := loadFactory(context) 52 if err != nil { 53 return nil, err 54 } 55 return factory.Load(id) 56 } 57 58 func fatalf(t string, v ...interface{}) { 59 fatal(fmt.Errorf(t, v...)) 60 } 61 62 func getDefaultImagePath(context *cli.Context) string { 63 cwd, err := os.Getwd() 64 if err != nil { 65 panic(err) 66 } 67 return filepath.Join(cwd, "checkpoint") 68 } 69 70 // newProcess returns a new libcontainer Process with the arguments from the 71 // spec and stdio from the current process. 72 func newProcess(p specs.Process) (*libcontainer.Process, error) { 73 lp := &libcontainer.Process{ 74 Args: p.Args, 75 Env: p.Env, 76 // TODO: fix libcontainer's API to better support uid/gid in a typesafe way. 77 User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID), 78 Cwd: p.Cwd, 79 Capabilities: p.Capabilities, 80 Label: p.SelinuxLabel, 81 NoNewPrivileges: &p.NoNewPrivileges, 82 AppArmorProfile: p.ApparmorProfile, 83 } 84 for _, gid := range p.User.AdditionalGids { 85 lp.AdditionalGroups = append(lp.AdditionalGroups, strconv.FormatUint(uint64(gid), 10)) 86 } 87 for _, rlimit := range p.Rlimits { 88 rl, err := createLibContainerRlimit(rlimit) 89 if err != nil { 90 return nil, err 91 } 92 lp.Rlimits = append(lp.Rlimits, rl) 93 } 94 return lp, nil 95 } 96 97 func dupStdio(process *libcontainer.Process, rootuid, rootgid int) error { 98 process.Stdin = os.Stdin 99 process.Stdout = os.Stdout 100 process.Stderr = os.Stderr 101 for _, fd := range []uintptr{ 102 os.Stdin.Fd(), 103 os.Stdout.Fd(), 104 os.Stderr.Fd(), 105 } { 106 if err := syscall.Fchown(int(fd), rootuid, rootgid); err != nil { 107 return err 108 } 109 } 110 return nil 111 } 112 113 // If systemd is supporting sd_notify protocol, this function will add support 114 // for sd_notify protocol from within the container. 115 func setupSdNotify(spec *specs.Spec, notifySocket string) { 116 spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}}) 117 spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket)) 118 } 119 120 func destroy(container libcontainer.Container) { 121 if err := container.Destroy(); err != nil { 122 logrus.Error(err) 123 } 124 } 125 126 // setupIO sets the proper IO on the process depending on the configuration 127 // If there is a nil error then there must be a non nil tty returned 128 func setupIO(process *libcontainer.Process, rootuid, rootgid int, console string, createTTY, detach bool) (*tty, error) { 129 // detach and createTty will not work unless a console path is passed 130 // so error out here before changing any terminal settings 131 if createTTY && detach && console == "" { 132 return nil, fmt.Errorf("cannot allocate tty if runc will detach") 133 } 134 if createTTY { 135 return createTty(process, rootuid, rootgid, console) 136 } 137 if detach { 138 if err := dupStdio(process, rootuid, rootgid); err != nil { 139 return nil, err 140 } 141 return &tty{}, nil 142 } 143 return createStdioPipes(process, rootuid, rootgid) 144 } 145 146 // createPidFile creates a file with the processes pid inside it atomically 147 // it creates a temp file with the paths filename + '.' infront of it 148 // then renames the file 149 func createPidFile(path string, process *libcontainer.Process) error { 150 pid, err := process.Pid() 151 if err != nil { 152 return err 153 } 154 var ( 155 tmpDir = filepath.Dir(path) 156 tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path))) 157 ) 158 f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) 159 if err != nil { 160 return err 161 } 162 _, err = fmt.Fprintf(f, "%d", pid) 163 f.Close() 164 if err != nil { 165 return err 166 } 167 return os.Rename(tmpName, path) 168 } 169 170 func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) { 171 config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{ 172 CgroupName: id, 173 UseSystemdCgroup: context.GlobalBool("systemd-cgroup"), 174 NoPivotRoot: context.Bool("no-pivot"), 175 NoNewKeyring: context.Bool("no-new-keyring"), 176 Spec: spec, 177 }) 178 if err != nil { 179 return nil, err 180 } 181 182 factory, err := loadFactory(context) 183 if err != nil { 184 return nil, err 185 } 186 return factory.Create(id, config) 187 } 188 189 type runner struct { 190 enableSubreaper bool 191 shouldDestroy bool 192 detach bool 193 listenFDs []*os.File 194 pidFile string 195 console string 196 container libcontainer.Container 197 create bool 198 } 199 200 func (r *runner) run(config *specs.Process) (int, error) { 201 process, err := newProcess(*config) 202 if err != nil { 203 r.destroy() 204 return -1, err 205 } 206 if len(r.listenFDs) > 0 { 207 process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(r.listenFDs)), "LISTEN_PID=1") 208 process.ExtraFiles = append(process.ExtraFiles, r.listenFDs...) 209 } 210 rootuid, err := r.container.Config().HostUID() 211 if err != nil { 212 r.destroy() 213 return -1, err 214 } 215 rootgid, err := r.container.Config().HostGID() 216 if err != nil { 217 r.destroy() 218 return -1, err 219 } 220 tty, err := setupIO(process, rootuid, rootgid, r.console, config.Terminal, r.detach || r.create) 221 if err != nil { 222 r.destroy() 223 return -1, err 224 } 225 handler := newSignalHandler(tty, r.enableSubreaper) 226 startFn := r.container.Start 227 if !r.create { 228 startFn = r.container.Run 229 } 230 defer tty.Close() 231 if err := startFn(process); err != nil { 232 r.destroy() 233 return -1, err 234 } 235 if err := tty.ClosePostStart(); err != nil { 236 r.terminate(process) 237 r.destroy() 238 return -1, err 239 } 240 if r.pidFile != "" { 241 if err := createPidFile(r.pidFile, process); err != nil { 242 r.terminate(process) 243 r.destroy() 244 return -1, err 245 } 246 } 247 if r.detach || r.create { 248 return 0, nil 249 } 250 status, err := handler.forward(process) 251 if err != nil { 252 r.terminate(process) 253 } 254 r.destroy() 255 return status, err 256 } 257 258 func (r *runner) destroy() { 259 if r.shouldDestroy { 260 destroy(r.container) 261 } 262 } 263 264 func (r *runner) terminate(p *libcontainer.Process) { 265 p.Signal(syscall.SIGKILL) 266 p.Wait() 267 } 268 269 func validateProcessSpec(spec *specs.Process) error { 270 if spec.Cwd == "" { 271 return fmt.Errorf("Cwd property must not be empty") 272 } 273 if !filepath.IsAbs(spec.Cwd) { 274 return fmt.Errorf("Cwd must be an absolute path") 275 } 276 if len(spec.Args) == 0 { 277 return fmt.Errorf("args must not be empty") 278 } 279 return nil 280 } 281 282 func startContainer(context *cli.Context, spec *specs.Spec, create bool) (int, error) { 283 id := context.Args().First() 284 if id == "" { 285 return -1, errEmptyID 286 } 287 container, err := createContainer(context, id, spec) 288 if err != nil { 289 return -1, err 290 } 291 // Support on-demand socket activation by passing file descriptors into the container init process. 292 listenFDs := []*os.File{} 293 if os.Getenv("LISTEN_FDS") != "" { 294 listenFDs = activation.Files(false) 295 } 296 r := &runner{ 297 enableSubreaper: !context.Bool("no-subreaper"), 298 shouldDestroy: true, 299 container: container, 300 listenFDs: listenFDs, 301 console: context.String("console"), 302 detach: context.Bool("detach"), 303 pidFile: context.String("pid-file"), 304 create: create, 305 } 306 return r.run(&spec.Process) 307 }