github.com/demonoid81/containerd@v1.3.4/runtime/v2/runc/container.go (about) 1 // +build linux 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package runc 20 21 import ( 22 "context" 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 "sync" 27 28 "github.com/containerd/cgroups" 29 "github.com/containerd/console" 30 "github.com/containerd/containerd/errdefs" 31 "github.com/containerd/containerd/mount" 32 "github.com/containerd/containerd/namespaces" 33 "github.com/containerd/containerd/pkg/process" 34 "github.com/containerd/containerd/pkg/stdio" 35 "github.com/containerd/containerd/runtime/v2/runc/options" 36 "github.com/containerd/containerd/runtime/v2/task" 37 "github.com/containerd/typeurl" 38 "github.com/pkg/errors" 39 "github.com/sirupsen/logrus" 40 ) 41 42 // NewContainer returns a new runc container 43 func NewContainer(ctx context.Context, platform stdio.Platform, r *task.CreateTaskRequest) (*Container, error) { 44 ns, err := namespaces.NamespaceRequired(ctx) 45 if err != nil { 46 return nil, errors.Wrap(err, "create namespace") 47 } 48 49 var opts options.Options 50 if r.Options != nil { 51 v, err := typeurl.UnmarshalAny(r.Options) 52 if err != nil { 53 return nil, err 54 } 55 opts = *v.(*options.Options) 56 } 57 58 var mounts []process.Mount 59 for _, m := range r.Rootfs { 60 mounts = append(mounts, process.Mount{ 61 Type: m.Type, 62 Source: m.Source, 63 Target: m.Target, 64 Options: m.Options, 65 }) 66 } 67 68 rootfs := "" 69 if len(mounts) > 0 { 70 rootfs = filepath.Join(r.Bundle, "rootfs") 71 if err := os.Mkdir(rootfs, 0711); err != nil && !os.IsExist(err) { 72 return nil, err 73 } 74 } 75 76 config := &process.CreateConfig{ 77 ID: r.ID, 78 Bundle: r.Bundle, 79 Runtime: opts.BinaryName, 80 Rootfs: mounts, 81 Terminal: r.Terminal, 82 Stdin: r.Stdin, 83 Stdout: r.Stdout, 84 Stderr: r.Stderr, 85 Checkpoint: r.Checkpoint, 86 ParentCheckpoint: r.ParentCheckpoint, 87 Options: r.Options, 88 } 89 90 if err := WriteRuntime(r.Bundle, opts.BinaryName); err != nil { 91 return nil, err 92 } 93 defer func() { 94 if err != nil { 95 if err2 := mount.UnmountAll(rootfs, 0); err2 != nil { 96 logrus.WithError(err2).Warn("failed to cleanup rootfs mount") 97 } 98 } 99 }() 100 for _, rm := range mounts { 101 m := &mount.Mount{ 102 Type: rm.Type, 103 Source: rm.Source, 104 Options: rm.Options, 105 } 106 if err := m.Mount(rootfs); err != nil { 107 return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m) 108 } 109 } 110 111 p, err := newInit( 112 ctx, 113 r.Bundle, 114 filepath.Join(r.Bundle, "work"), 115 ns, 116 platform, 117 config, 118 &opts, 119 rootfs, 120 ) 121 if err != nil { 122 return nil, errdefs.ToGRPC(err) 123 } 124 if err := p.Create(ctx, config); err != nil { 125 return nil, errdefs.ToGRPC(err) 126 } 127 container := &Container{ 128 ID: r.ID, 129 Bundle: r.Bundle, 130 process: p, 131 processes: make(map[string]process.Process), 132 reservedProcess: make(map[string]struct{}), 133 } 134 pid := p.Pid() 135 if pid > 0 { 136 cg, err := cgroups.Load(cgroups.V1, cgroups.PidPath(pid)) 137 if err != nil { 138 logrus.WithError(err).Errorf("loading cgroup for %d", pid) 139 } 140 container.cgroup = cg 141 } 142 return container, nil 143 } 144 145 // ReadRuntime reads the runtime information from the path 146 func ReadRuntime(path string) (string, error) { 147 data, err := ioutil.ReadFile(filepath.Join(path, "runtime")) 148 if err != nil { 149 return "", err 150 } 151 return string(data), nil 152 } 153 154 // WriteRuntime writes the runtime information into the path 155 func WriteRuntime(path, runtime string) error { 156 return ioutil.WriteFile(filepath.Join(path, "runtime"), []byte(runtime), 0600) 157 } 158 159 func newInit(ctx context.Context, path, workDir, namespace string, platform stdio.Platform, 160 r *process.CreateConfig, options *options.Options, rootfs string) (*process.Init, error) { 161 runtime := process.NewRunc(options.Root, path, namespace, options.BinaryName, options.CriuPath, options.SystemdCgroup) 162 p := process.New(r.ID, runtime, stdio.Stdio{ 163 Stdin: r.Stdin, 164 Stdout: r.Stdout, 165 Stderr: r.Stderr, 166 Terminal: r.Terminal, 167 }) 168 p.Bundle = r.Bundle 169 p.Platform = platform 170 p.Rootfs = rootfs 171 p.WorkDir = workDir 172 p.IoUID = int(options.IoUid) 173 p.IoGID = int(options.IoGid) 174 p.NoPivotRoot = options.NoPivotRoot 175 p.NoNewKeyring = options.NoNewKeyring 176 p.CriuWorkPath = options.CriuWorkPath 177 if p.CriuWorkPath == "" { 178 // if criu work path not set, use container WorkDir 179 p.CriuWorkPath = p.WorkDir 180 } 181 return p, nil 182 } 183 184 // Container for operating on a runc container and its processes 185 type Container struct { 186 mu sync.Mutex 187 188 // ID of the container 189 ID string 190 // Bundle path 191 Bundle string 192 193 cgroup cgroups.Cgroup 194 process process.Process 195 processes map[string]process.Process 196 reservedProcess map[string]struct{} 197 } 198 199 // All processes in the container 200 func (c *Container) All() (o []process.Process) { 201 c.mu.Lock() 202 defer c.mu.Unlock() 203 204 for _, p := range c.processes { 205 o = append(o, p) 206 } 207 if c.process != nil { 208 o = append(o, c.process) 209 } 210 return o 211 } 212 213 // ExecdProcesses added to the container 214 func (c *Container) ExecdProcesses() (o []process.Process) { 215 c.mu.Lock() 216 defer c.mu.Unlock() 217 for _, p := range c.processes { 218 o = append(o, p) 219 } 220 return o 221 } 222 223 // Pid of the main process of a container 224 func (c *Container) Pid() int { 225 c.mu.Lock() 226 defer c.mu.Unlock() 227 return c.process.Pid() 228 } 229 230 // Cgroup of the container 231 func (c *Container) Cgroup() cgroups.Cgroup { 232 c.mu.Lock() 233 defer c.mu.Unlock() 234 return c.cgroup 235 } 236 237 // CgroupSet sets the cgroup to the container 238 func (c *Container) CgroupSet(cg cgroups.Cgroup) { 239 c.mu.Lock() 240 c.cgroup = cg 241 c.mu.Unlock() 242 } 243 244 // Process returns the process by id 245 func (c *Container) Process(id string) (process.Process, error) { 246 c.mu.Lock() 247 defer c.mu.Unlock() 248 if id == "" { 249 if c.process == nil { 250 return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "container must be created") 251 } 252 return c.process, nil 253 } 254 p, ok := c.processes[id] 255 if !ok { 256 return nil, errors.Wrapf(errdefs.ErrNotFound, "process does not exist %s", id) 257 } 258 return p, nil 259 } 260 261 // ReserveProcess checks for the existence of an id and atomically 262 // reserves the process id if it does not already exist 263 // 264 // Returns true if the process id was successfully reserved and a 265 // cancel func to release the reservation 266 func (c *Container) ReserveProcess(id string) (bool, func()) { 267 c.mu.Lock() 268 defer c.mu.Unlock() 269 270 if _, ok := c.processes[id]; ok { 271 return false, nil 272 } 273 if _, ok := c.reservedProcess[id]; ok { 274 return false, nil 275 } 276 c.reservedProcess[id] = struct{}{} 277 return true, func() { 278 c.mu.Lock() 279 defer c.mu.Unlock() 280 delete(c.reservedProcess, id) 281 } 282 } 283 284 // ProcessAdd adds a new process to the container 285 func (c *Container) ProcessAdd(process process.Process) { 286 c.mu.Lock() 287 defer c.mu.Unlock() 288 289 delete(c.reservedProcess, process.ID()) 290 c.processes[process.ID()] = process 291 } 292 293 // ProcessRemove removes the process by id from the container 294 func (c *Container) ProcessRemove(id string) { 295 c.mu.Lock() 296 defer c.mu.Unlock() 297 delete(c.processes, id) 298 } 299 300 // Start a container process 301 func (c *Container) Start(ctx context.Context, r *task.StartRequest) (process.Process, error) { 302 p, err := c.Process(r.ExecID) 303 if err != nil { 304 return nil, err 305 } 306 if err := p.Start(ctx); err != nil { 307 return nil, err 308 } 309 if c.Cgroup() == nil && p.Pid() > 0 { 310 cg, err := cgroups.Load(cgroups.V1, cgroups.PidPath(p.Pid())) 311 if err != nil { 312 logrus.WithError(err).Errorf("loading cgroup for %d", p.Pid()) 313 } 314 c.cgroup = cg 315 } 316 return p, nil 317 } 318 319 // Delete the container or a process by id 320 func (c *Container) Delete(ctx context.Context, r *task.DeleteRequest) (process.Process, error) { 321 p, err := c.Process(r.ExecID) 322 if err != nil { 323 return nil, err 324 } 325 if err := p.Delete(ctx); err != nil { 326 return nil, err 327 } 328 if r.ExecID != "" { 329 c.ProcessRemove(r.ExecID) 330 } 331 return p, nil 332 } 333 334 // Exec an additional process 335 func (c *Container) Exec(ctx context.Context, r *task.ExecProcessRequest) (process.Process, error) { 336 process, err := c.process.(*process.Init).Exec(ctx, c.Bundle, &process.ExecConfig{ 337 ID: r.ExecID, 338 Terminal: r.Terminal, 339 Stdin: r.Stdin, 340 Stdout: r.Stdout, 341 Stderr: r.Stderr, 342 Spec: r.Spec, 343 }) 344 if err != nil { 345 return nil, err 346 } 347 c.ProcessAdd(process) 348 return process, nil 349 } 350 351 // Pause the container 352 func (c *Container) Pause(ctx context.Context) error { 353 return c.process.(*process.Init).Pause(ctx) 354 } 355 356 // Resume the container 357 func (c *Container) Resume(ctx context.Context) error { 358 return c.process.(*process.Init).Resume(ctx) 359 } 360 361 // ResizePty of a process 362 func (c *Container) ResizePty(ctx context.Context, r *task.ResizePtyRequest) error { 363 p, err := c.Process(r.ExecID) 364 if err != nil { 365 return err 366 } 367 ws := console.WinSize{ 368 Width: uint16(r.Width), 369 Height: uint16(r.Height), 370 } 371 return p.Resize(ws) 372 } 373 374 // Kill a process 375 func (c *Container) Kill(ctx context.Context, r *task.KillRequest) error { 376 p, err := c.Process(r.ExecID) 377 if err != nil { 378 return err 379 } 380 return p.Kill(ctx, r.Signal, r.All) 381 } 382 383 // CloseIO of a process 384 func (c *Container) CloseIO(ctx context.Context, r *task.CloseIORequest) error { 385 p, err := c.Process(r.ExecID) 386 if err != nil { 387 return err 388 } 389 if stdin := p.Stdin(); stdin != nil { 390 if err := stdin.Close(); err != nil { 391 return errors.Wrap(err, "close stdin") 392 } 393 } 394 return nil 395 } 396 397 // Checkpoint the container 398 func (c *Container) Checkpoint(ctx context.Context, r *task.CheckpointTaskRequest) error { 399 p, err := c.Process("") 400 if err != nil { 401 return err 402 } 403 var opts options.CheckpointOptions 404 if r.Options != nil { 405 v, err := typeurl.UnmarshalAny(r.Options) 406 if err != nil { 407 return err 408 } 409 opts = *v.(*options.CheckpointOptions) 410 } 411 return p.(*process.Init).Checkpoint(ctx, &process.CheckpointConfig{ 412 Path: r.Path, 413 Exit: opts.Exit, 414 AllowOpenTCP: opts.OpenTcp, 415 AllowExternalUnixSockets: opts.ExternalUnixSockets, 416 AllowTerminal: opts.Terminal, 417 FileLocks: opts.FileLocks, 418 EmptyNamespaces: opts.EmptyNamespaces, 419 WorkDir: opts.WorkPath, 420 }) 421 } 422 423 // Update the resource information of a running container 424 func (c *Container) Update(ctx context.Context, r *task.UpdateTaskRequest) error { 425 p, err := c.Process("") 426 if err != nil { 427 return err 428 } 429 return p.(*process.Init).Update(ctx, r.Resources) 430 } 431 432 // HasPid returns true if the container owns a specific pid 433 func (c *Container) HasPid(pid int) bool { 434 if c.Pid() == pid { 435 return true 436 } 437 for _, p := range c.All() { 438 if p.Pid() == pid { 439 return true 440 } 441 } 442 return false 443 }