github.com/containers/podman/v4@v4.9.4/libpod/container_api.go (about) 1 //go:build !remote 2 // +build !remote 3 4 package libpod 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "io" 11 "net/http" 12 "os" 13 "sync" 14 "time" 15 16 "github.com/containers/common/pkg/resize" 17 "github.com/containers/podman/v4/libpod/define" 18 "github.com/containers/podman/v4/libpod/events" 19 "github.com/containers/podman/v4/pkg/signal" 20 "github.com/containers/storage/pkg/archive" 21 spec "github.com/opencontainers/runtime-spec/specs-go" 22 "github.com/sirupsen/logrus" 23 "golang.org/x/sys/unix" 24 ) 25 26 // Init creates a container in the OCI runtime, moving a container from 27 // ContainerStateConfigured, ContainerStateStopped, or ContainerStateExited to 28 // ContainerStateCreated. Once in Created state, Conmon will be running, which 29 // allows the container to be attached to. The container can subsequently 30 // transition to ContainerStateRunning via Start(), or be transitioned back to 31 // ContainerStateConfigured by Cleanup() (which will stop conmon and unmount the 32 // container). 33 // Init requires that all dependency containers be started (e.g. pod infra 34 // containers). The `recursive` parameter will, if set to true, start these 35 // dependency containers before initializing this container. 36 func (c *Container) Init(ctx context.Context, recursive bool) error { 37 if !c.batched { 38 c.lock.Lock() 39 defer c.lock.Unlock() 40 41 if err := c.syncContainer(); err != nil { 42 return err 43 } 44 } 45 46 if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateStopped, define.ContainerStateExited) { 47 return fmt.Errorf("container %s has already been created in runtime: %w", c.ID(), define.ErrCtrStateInvalid) 48 } 49 50 if !recursive { 51 if err := c.checkDependenciesAndHandleError(); err != nil { 52 return err 53 } 54 } else { 55 if err := c.startDependencies(ctx); err != nil { 56 return err 57 } 58 } 59 60 if err := c.prepare(); err != nil { 61 if err2 := c.cleanup(ctx); err2 != nil { 62 logrus.Errorf("Cleaning up container %s: %v", c.ID(), err2) 63 } 64 return err 65 } 66 67 if c.state.State == define.ContainerStateStopped { 68 // Reinitialize the container 69 return c.reinit(ctx, false) 70 } 71 72 // Initialize the container for the first time 73 return c.init(ctx, false) 74 } 75 76 // Start starts the given container. 77 // Start will accept container in ContainerStateConfigured, 78 // ContainerStateCreated, ContainerStateStopped, and ContainerStateExited, and 79 // transition them to ContainerStateRunning (all containers not in 80 // ContainerStateCreated will make an intermediate stop there via the Init API). 81 // Once in ContainerStateRunning, the container can be transitioned to 82 // ContainerStatePaused via Pause(), or to ContainerStateStopped by the process 83 // stopping (either due to exit, or being forced to stop by the Kill or Stop API 84 // calls). 85 // Start requires that all dependency containers (e.g. pod infra containers) are 86 // running before starting the container. The recursive parameter, if set, will start all 87 // dependencies before starting this container. 88 func (c *Container) Start(ctx context.Context, recursive bool) (finalErr error) { 89 defer func() { 90 if finalErr != nil { 91 // Have to re-lock. 92 // As this is the first defer, it's the last thing to 93 // happen in the function - so `defer c.lock.Unlock()` 94 // below already fired. 95 if !c.batched { 96 c.lock.Lock() 97 defer c.lock.Unlock() 98 } 99 100 if err := saveContainerError(c, finalErr); err != nil { 101 logrus.Debug(err) 102 } 103 } 104 }() 105 106 if !c.batched { 107 c.lock.Lock() 108 defer c.lock.Unlock() 109 110 if err := c.syncContainer(); err != nil { 111 return err 112 } 113 } 114 if err := c.prepareToStart(ctx, recursive); err != nil { 115 return err 116 } 117 118 // Start the container 119 return c.start(ctx) 120 } 121 122 // Update updates the given container. 123 // only the cgroup config can be updated and therefore only a linux resource spec is passed. 124 func (c *Container) Update(res *spec.LinuxResources) error { 125 if err := c.syncContainer(); err != nil { 126 return err 127 } 128 return c.update(res) 129 } 130 131 // StartAndAttach starts a container and attaches to it. 132 // This acts as a combination of the Start and Attach APIs, ensuring proper 133 // ordering of the two such that no output from the container is lost (e.g. the 134 // Attach call occurs before Start). 135 // In overall functionality, it is identical to the Start call, with the added 136 // side effect that an attach session will also be started. 137 func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan resize.TerminalSize, recursive bool) (retChan <-chan error, finalErr error) { 138 defer func() { 139 if finalErr != nil { 140 // Have to re-lock. 141 // As this is the first defer, it's the last thing to 142 // happen in the function - so `defer c.lock.Unlock()` 143 // below already fired. 144 if !c.batched { 145 c.lock.Lock() 146 defer c.lock.Unlock() 147 } 148 149 if err := saveContainerError(c, finalErr); err != nil { 150 logrus.Debug(err) 151 } 152 } 153 }() 154 155 if !c.batched { 156 c.lock.Lock() 157 defer c.lock.Unlock() 158 159 if err := c.syncContainer(); err != nil { 160 return nil, err 161 } 162 } 163 164 if err := c.prepareToStart(ctx, recursive); err != nil { 165 return nil, err 166 } 167 attachChan := make(chan error) 168 169 // We need to ensure that we don't return until start() fired in attach. 170 // Use a channel to sync 171 startedChan := make(chan bool) 172 173 // Attach to the container before starting it 174 go func() { 175 // Start resizing 176 if c.LogDriver() != define.PassthroughLogging { 177 registerResizeFunc(resize, c.bundlePath()) 178 } 179 180 opts := new(AttachOptions) 181 opts.Streams = streams 182 opts.DetachKeys = &keys 183 opts.Start = true 184 opts.Started = startedChan 185 186 if err := c.ociRuntime.Attach(c, opts); err != nil { 187 attachChan <- err 188 } 189 close(attachChan) 190 }() 191 192 select { 193 case err := <-attachChan: 194 return nil, err 195 case <-startedChan: 196 c.newContainerEvent(events.Attach) 197 } 198 199 return attachChan, nil 200 } 201 202 // RestartWithTimeout restarts a running container and takes a given timeout in uint 203 func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) error { 204 if !c.batched { 205 c.lock.Lock() 206 defer c.lock.Unlock() 207 208 if err := c.syncContainer(); err != nil { 209 return err 210 } 211 } 212 213 if err := c.checkDependenciesAndHandleError(); err != nil { 214 return err 215 } 216 217 return c.restartWithTimeout(ctx, timeout) 218 } 219 220 // Stop uses the container's stop signal (or SIGTERM if no signal was specified) 221 // to stop the container, and if it has not stopped after container's stop 222 // timeout, SIGKILL is used to attempt to forcibly stop the container 223 // Default stop timeout is 10 seconds, but can be overridden when the container 224 // is created 225 func (c *Container) Stop() error { 226 // Stop with the container's given timeout 227 return c.StopWithTimeout(c.config.StopTimeout) 228 } 229 230 // StopWithTimeout is a version of Stop that allows a timeout to be specified 231 // manually. If timeout is 0, SIGKILL will be used immediately to kill the 232 // container. 233 func (c *Container) StopWithTimeout(timeout uint) (finalErr error) { 234 defer func() { 235 if finalErr != nil { 236 // Have to re-lock. 237 // As this is the first defer, it's the last thing to 238 // happen in the function - so `defer c.lock.Unlock()` 239 // below already fired. 240 if !c.batched { 241 c.lock.Lock() 242 defer c.lock.Unlock() 243 } 244 245 if err := saveContainerError(c, finalErr); err != nil { 246 logrus.Debug(err) 247 } 248 } 249 }() 250 251 if !c.batched { 252 c.lock.Lock() 253 defer c.lock.Unlock() 254 255 if err := c.syncContainer(); err != nil { 256 return err 257 } 258 } 259 260 return c.stop(timeout) 261 } 262 263 // Kill sends a signal to a container 264 func (c *Container) Kill(signal uint) error { 265 if !c.batched { 266 c.lock.Lock() 267 defer c.lock.Unlock() 268 269 if err := c.syncContainer(); err != nil { 270 return err 271 } 272 } 273 274 switch c.state.State { 275 case define.ContainerStateRunning, define.ContainerStateStopping, define.ContainerStatePaused: 276 // Note that killing containers in "stopping" state is okay. 277 // In that state, the Podman is waiting for the runtime to 278 // stop the container and if that is taking too long, a user 279 // may have decided to kill the container after all. 280 default: 281 return fmt.Errorf("can only kill running containers. %s is in state %s: %w", c.ID(), c.state.State.String(), define.ErrCtrStateInvalid) 282 } 283 284 // Hardcode all = false, we only use all when removing. 285 if err := c.ociRuntime.KillContainer(c, signal, false); err != nil { 286 return err 287 } 288 289 c.state.StoppedByUser = true 290 291 c.newContainerEvent(events.Kill) 292 293 // Make sure to wait for the container to exit in case of SIGKILL. 294 if signal == uint(unix.SIGKILL) { 295 return c.waitForConmonToExitAndSave() 296 } 297 298 return c.save() 299 } 300 301 // Attach attaches to a container. 302 // This function returns when the attach finishes. It does not hold the lock for 303 // the duration of its runtime, only using it at the beginning to verify state. 304 func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-chan resize.TerminalSize) error { 305 if c.LogDriver() == define.PassthroughLogging { 306 return fmt.Errorf("this container is using the 'passthrough' log driver, cannot attach: %w", define.ErrNoLogs) 307 } 308 if !c.batched { 309 c.lock.Lock() 310 if err := c.syncContainer(); err != nil { 311 c.lock.Unlock() 312 return err 313 } 314 // We are NOT holding the lock for the duration of the function. 315 c.lock.Unlock() 316 } 317 318 if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { 319 return fmt.Errorf("can only attach to created or running containers: %w", define.ErrCtrStateInvalid) 320 } 321 322 // HACK: This is really gross, but there isn't a better way without 323 // splitting attach into separate versions for StartAndAttach and normal 324 // attaching, and I really do not want to do that right now. 325 // Send a SIGWINCH after attach succeeds so that most programs will 326 // redraw the screen for the new attach session. 327 attachRdy := make(chan bool, 1) 328 if c.Terminal() { 329 go func() { 330 <-attachRdy 331 c.lock.Lock() 332 defer c.lock.Unlock() 333 if err := c.ociRuntime.KillContainer(c, uint(signal.SIGWINCH), false); err != nil { 334 logrus.Warnf("Unable to send SIGWINCH to container %s after attach: %v", c.ID(), err) 335 } 336 }() 337 } 338 339 // Start resizing 340 if c.LogDriver() != define.PassthroughLogging { 341 registerResizeFunc(resize, c.bundlePath()) 342 } 343 344 opts := new(AttachOptions) 345 opts.Streams = streams 346 opts.DetachKeys = &keys 347 opts.AttachReady = attachRdy 348 349 c.newContainerEvent(events.Attach) 350 return c.ociRuntime.Attach(c, opts) 351 } 352 353 // HTTPAttach forwards an attach session over a hijacked HTTP session. 354 // HTTPAttach will consume and close the included httpCon, which is expected to 355 // be sourced from a hijacked HTTP connection. 356 // The cancel channel is optional, and can be used to asynchronously cancel the 357 // attach session. 358 // The streams variable is only supported if the container was not a terminal, 359 // and allows specifying which of the container's standard streams will be 360 // forwarded to the client. 361 // This function returns when the attach finishes. It does not hold the lock for 362 // the duration of its runtime, only using it at the beginning to verify state. 363 // The streamLogs parameter indicates that all the container's logs until present 364 // will be streamed at the beginning of the attach. 365 // The streamAttach parameter indicates that the attach itself will be streamed 366 // over the socket; if this is not set, but streamLogs is, only the logs will be 367 // sent. 368 // At least one of streamAttach and streamLogs must be set. 369 func (c *Container) HTTPAttach(r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, streamAttach, streamLogs bool, hijackDone chan<- bool) error { 370 // Ensure we don't leak a goroutine if we exit before hijack completes. 371 defer func() { 372 close(hijackDone) 373 }() 374 375 if !c.batched { 376 c.lock.Lock() 377 if err := c.syncContainer(); err != nil { 378 c.lock.Unlock() 379 380 return err 381 } 382 // We are NOT holding the lock for the duration of the function. 383 c.lock.Unlock() 384 } 385 386 if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { 387 return fmt.Errorf("can only attach to created or running containers: %w", define.ErrCtrStateInvalid) 388 } 389 390 if !streamAttach && !streamLogs { 391 return fmt.Errorf("must specify at least one of stream or logs: %w", define.ErrInvalidArg) 392 } 393 394 logrus.Infof("Performing HTTP Hijack attach to container %s", c.ID()) 395 396 c.newContainerEvent(events.Attach) 397 return c.ociRuntime.HTTPAttach(c, r, w, streams, detachKeys, cancel, hijackDone, streamAttach, streamLogs) 398 } 399 400 // AttachResize resizes the container's terminal, which is displayed by Attach 401 // and HTTPAttach. 402 func (c *Container) AttachResize(newSize resize.TerminalSize) error { 403 if !c.batched { 404 c.lock.Lock() 405 defer c.lock.Unlock() 406 407 if err := c.syncContainer(); err != nil { 408 return err 409 } 410 } 411 412 if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { 413 return fmt.Errorf("can only resize created or running containers: %w", define.ErrCtrStateInvalid) 414 } 415 416 logrus.Infof("Resizing TTY of container %s", c.ID()) 417 418 return c.ociRuntime.AttachResize(c, newSize) 419 } 420 421 // Mount mounts a container's filesystem on the host 422 // The path where the container has been mounted is returned 423 func (c *Container) Mount() (string, error) { 424 if !c.batched { 425 c.lock.Lock() 426 defer c.lock.Unlock() 427 428 if err := c.syncContainer(); err != nil { 429 return "", err 430 } 431 } 432 433 defer c.newContainerEvent(events.Mount) 434 return c.mount() 435 } 436 437 // Unmount unmounts a container's filesystem on the host 438 func (c *Container) Unmount(force bool) error { 439 if !c.batched { 440 c.lock.Lock() 441 defer c.lock.Unlock() 442 443 if err := c.syncContainer(); err != nil { 444 return err 445 } 446 } 447 if c.state.Mounted { 448 mounted, err := c.runtime.storageService.MountedContainerImage(c.ID()) 449 if err != nil { 450 return fmt.Errorf("can't determine how many times %s is mounted, refusing to unmount: %w", c.ID(), err) 451 } 452 if mounted == 1 { 453 if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { 454 return fmt.Errorf("cannot unmount storage for container %s as it is running or paused: %w", c.ID(), define.ErrCtrStateInvalid) 455 } 456 execSessions, err := c.getActiveExecSessions() 457 if err != nil { 458 return err 459 } 460 if len(execSessions) != 0 { 461 return fmt.Errorf("container %s has active exec sessions, refusing to unmount: %w", c.ID(), define.ErrCtrStateInvalid) 462 } 463 return fmt.Errorf("can't unmount %s last mount, it is still in use: %w", c.ID(), define.ErrInternal) 464 } 465 } 466 defer c.newContainerEvent(events.Unmount) 467 return c.unmount(force) 468 } 469 470 // Pause pauses a container 471 func (c *Container) Pause() error { 472 if !c.batched { 473 c.lock.Lock() 474 defer c.lock.Unlock() 475 476 if err := c.syncContainer(); err != nil { 477 return err 478 } 479 } 480 481 if c.state.State == define.ContainerStatePaused { 482 return fmt.Errorf("%q is already paused: %w", c.ID(), define.ErrCtrStateInvalid) 483 } 484 if c.state.State != define.ContainerStateRunning { 485 return fmt.Errorf("%q is not running, can't pause: %w", c.state.State, define.ErrCtrStateInvalid) 486 } 487 defer c.newContainerEvent(events.Pause) 488 return c.pause() 489 } 490 491 // Unpause unpauses a container 492 func (c *Container) Unpause() error { 493 if !c.batched { 494 c.lock.Lock() 495 defer c.lock.Unlock() 496 497 if err := c.syncContainer(); err != nil { 498 return err 499 } 500 } 501 502 if c.state.State != define.ContainerStatePaused { 503 return fmt.Errorf("%q is not paused, can't unpause: %w", c.ID(), define.ErrCtrStateInvalid) 504 } 505 defer c.newContainerEvent(events.Unpause) 506 return c.unpause() 507 } 508 509 // Export exports a container's root filesystem as a tar archive 510 // The archive will be saved as a file at the given path 511 func (c *Container) Export(out io.Writer) error { 512 if !c.batched { 513 c.lock.Lock() 514 defer c.lock.Unlock() 515 516 if err := c.syncContainer(); err != nil { 517 return err 518 } 519 } 520 521 if c.state.State == define.ContainerStateRemoving { 522 return fmt.Errorf("cannot mount container %s as it is being removed: %w", c.ID(), define.ErrCtrStateInvalid) 523 } 524 525 defer c.newContainerEvent(events.Mount) 526 return c.export(out) 527 } 528 529 // AddArtifact creates and writes to an artifact file for the container 530 func (c *Container) AddArtifact(name string, data []byte) error { 531 if !c.valid { 532 return define.ErrCtrRemoved 533 } 534 535 return os.WriteFile(c.getArtifactPath(name), data, 0o740) 536 } 537 538 // GetArtifact reads the specified artifact file from the container 539 func (c *Container) GetArtifact(name string) ([]byte, error) { 540 if !c.valid { 541 return nil, define.ErrCtrRemoved 542 } 543 544 return os.ReadFile(c.getArtifactPath(name)) 545 } 546 547 // RemoveArtifact deletes the specified artifacts file 548 func (c *Container) RemoveArtifact(name string) error { 549 if !c.valid { 550 return define.ErrCtrRemoved 551 } 552 553 return os.Remove(c.getArtifactPath(name)) 554 } 555 556 // Wait blocks until the container exits and returns its exit code. 557 func (c *Container) Wait(ctx context.Context) (int32, error) { 558 return c.WaitForExit(ctx, DefaultWaitInterval) 559 } 560 561 // WaitForExit blocks until the container exits and returns its exit code. The 562 // argument is the interval at which checks the container's status. 563 func (c *Container) WaitForExit(ctx context.Context, pollInterval time.Duration) (int32, error) { 564 if !c.valid { 565 return -1, define.ErrCtrRemoved 566 } 567 568 id := c.ID() 569 var conmonTimer time.Timer 570 conmonTimerSet := false 571 572 conmonPidFd := c.getConmonPidFd() 573 if conmonPidFd != -1 { 574 defer unix.Close(conmonPidFd) 575 } 576 conmonPidFdTriggered := false 577 578 getExitCode := func() (bool, int32, error) { 579 containerRemoved := false 580 if !c.batched { 581 c.lock.Lock() 582 defer c.lock.Unlock() 583 } 584 585 if err := c.syncContainer(); err != nil { 586 if !errors.Is(err, define.ErrNoSuchCtr) { 587 return false, -1, err 588 } 589 containerRemoved = true 590 } 591 592 // If conmon is not alive anymore set a timer to make sure 593 // we're returning even if conmon has forcefully been killed. 594 if !conmonTimerSet && !containerRemoved { 595 conmonAlive, err := c.ociRuntime.CheckConmonRunning(c) 596 switch { 597 case errors.Is(err, define.ErrNoSuchCtr): 598 // Container has been removed, so we assume the 599 // exit code is present in the DB. 600 containerRemoved = true 601 case err != nil: 602 return false, -1, err 603 case !conmonAlive: 604 // Give the exit code at most 20 seconds to 605 // show up in the DB. That should largely be 606 // enough for the cleanup process. 607 timerDuration := time.Second * 20 608 conmonTimer = *time.NewTimer(timerDuration) 609 conmonTimerSet = true 610 case conmonAlive: 611 // Continue waiting if conmon's still running. 612 return false, -1, nil 613 } 614 } 615 616 timedout := "" 617 if !containerRemoved { 618 // If conmon is dead for more than $timerDuration or if the 619 // container has exited properly, try to look up the exit code. 620 select { 621 case <-conmonTimer.C: 622 logrus.Debugf("Exceeded conmon timeout waiting for container %s to exit", id) 623 timedout = " [exceeded conmon timeout waiting for container to exit]" 624 default: 625 switch c.state.State { 626 case define.ContainerStateExited, define.ContainerStateConfigured: 627 // Container exited, so we can look up the exit code. 628 case define.ContainerStateStopped: 629 // Continue looping unless the restart policy is always. 630 // In this case, the container would never transition to 631 // the exited state, so we need to look up the exit code. 632 if c.config.RestartPolicy != define.RestartPolicyAlways { 633 return false, -1, nil 634 } 635 default: 636 // Continue looping 637 return false, -1, nil 638 } 639 } 640 } 641 642 exitCode, err := c.runtime.state.GetContainerExitCode(id) 643 if err != nil { 644 if errors.Is(err, define.ErrNoSuchExitCode) { 645 // If the container is configured or created, we must assume it never ran. 646 if c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated) { 647 return true, 0, nil 648 } 649 } 650 return true, -1, fmt.Errorf("%w (container in state %s)%s", err, c.state.State, timedout) 651 } 652 653 return true, exitCode, nil 654 } 655 656 for { 657 hasExited, exitCode, err := getExitCode() 658 if hasExited { 659 return exitCode, err 660 } 661 if err != nil { 662 return -1, err 663 } 664 select { 665 case <-ctx.Done(): 666 return -1, fmt.Errorf("waiting for exit code of container %s canceled", id) 667 default: 668 if conmonPidFd != -1 && !conmonPidFdTriggered { 669 // If possible (pidfd works), the first cycle we block until conmon dies 670 // If this happens, and we fall back to the old poll delay 671 // There is a deadlock in the cleanup code for "play kube" which causes 672 // conmon to not exit, so unfortunately we have to use the poll interval 673 // timeout here to avoid hanging. 674 fds := []unix.PollFd{{Fd: int32(conmonPidFd), Events: unix.POLLIN}} 675 _, _ = unix.Poll(fds, int(pollInterval.Milliseconds())) 676 conmonPidFdTriggered = true 677 } else { 678 time.Sleep(pollInterval) 679 } 680 } 681 } 682 } 683 684 type waitResult struct { 685 code int32 686 err error 687 } 688 689 func (c *Container) WaitForConditionWithInterval(ctx context.Context, waitTimeout time.Duration, conditions ...string) (int32, error) { 690 if !c.valid { 691 return -1, define.ErrCtrRemoved 692 } 693 694 if len(conditions) == 0 { 695 panic("at least one condition should be passed") 696 } 697 698 ctx, cancelFn := context.WithCancel(ctx) 699 defer cancelFn() 700 701 resultChan := make(chan waitResult) 702 waitForExit := false 703 wantedStates := make(map[define.ContainerStatus]bool, len(conditions)) 704 wantedHealthStates := make(map[string]bool) 705 706 for _, rawCondition := range conditions { 707 switch rawCondition { 708 case define.HealthCheckHealthy, define.HealthCheckUnhealthy: 709 if !c.HasHealthCheck() { 710 return -1, fmt.Errorf("cannot use condition %q: container %s has no healthcheck", rawCondition, c.ID()) 711 } 712 wantedHealthStates[rawCondition] = true 713 default: 714 condition, err := define.StringToContainerStatus(rawCondition) 715 if err != nil { 716 return -1, err 717 } 718 switch condition { 719 case define.ContainerStateExited, define.ContainerStateStopped: 720 waitForExit = true 721 default: 722 wantedStates[condition] = true 723 } 724 } 725 } 726 727 trySend := func(code int32, err error) { 728 select { 729 case resultChan <- waitResult{code, err}: 730 case <-ctx.Done(): 731 } 732 } 733 734 var wg sync.WaitGroup 735 736 if waitForExit { 737 wg.Add(1) 738 go func() { 739 defer wg.Done() 740 741 code, err := c.WaitForExit(ctx, waitTimeout) 742 trySend(code, err) 743 }() 744 } 745 746 if len(wantedStates) > 0 || len(wantedHealthStates) > 0 { 747 wg.Add(1) 748 go func() { 749 defer wg.Done() 750 751 for { 752 if len(wantedStates) > 0 { 753 state, err := c.State() 754 if err != nil { 755 trySend(-1, err) 756 return 757 } 758 if _, found := wantedStates[state]; found { 759 trySend(-1, nil) 760 return 761 } 762 } 763 if len(wantedHealthStates) > 0 { 764 status, err := c.HealthCheckStatus() 765 if err != nil { 766 trySend(-1, err) 767 return 768 } 769 if _, found := wantedHealthStates[status]; found { 770 trySend(-1, nil) 771 return 772 } 773 } 774 select { 775 case <-ctx.Done(): 776 return 777 case <-time.After(waitTimeout): 778 continue 779 } 780 } 781 }() 782 } 783 784 var result waitResult 785 select { 786 case result = <-resultChan: 787 cancelFn() 788 case <-ctx.Done(): 789 result = waitResult{-1, define.ErrCanceled} 790 } 791 wg.Wait() 792 return result.code, result.err 793 } 794 795 // Cleanup unmounts all mount points in container and cleans up container storage 796 // It also cleans up the network stack 797 func (c *Container) Cleanup(ctx context.Context) error { 798 if !c.batched { 799 c.lock.Lock() 800 defer c.lock.Unlock() 801 802 if err := c.syncContainer(); err != nil { 803 // When the container has already been removed, the OCI runtime directory remains. 804 if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) { 805 if err := c.cleanupRuntime(ctx); err != nil { 806 return fmt.Errorf("cleaning up container %s from OCI runtime: %w", c.ID(), err) 807 } 808 return nil 809 } 810 logrus.Errorf("Syncing container %s status: %v", c.ID(), err) 811 return err 812 } 813 } 814 815 // Check if state is good 816 if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateStopping, define.ContainerStateExited) { 817 return fmt.Errorf("container %s is running or paused, refusing to clean up: %w", c.ID(), define.ErrCtrStateInvalid) 818 } 819 820 // if the container was not created in the oci runtime or was already cleaned up, then do nothing 821 if c.ensureState(define.ContainerStateConfigured, define.ContainerStateExited) { 822 return nil 823 } 824 825 // Handle restart policy. 826 // Returns a bool indicating whether we actually restarted. 827 // If we did, don't proceed to cleanup - just exit. 828 didRestart, err := c.handleRestartPolicy(ctx) 829 if err != nil { 830 return err 831 } 832 if didRestart { 833 return nil 834 } 835 836 // If we didn't restart, we perform a normal cleanup 837 838 // make sure all the container processes are terminated if we are running without a pid namespace. 839 hasPidNs := false 840 if c.config.Spec.Linux != nil { 841 for _, i := range c.config.Spec.Linux.Namespaces { 842 if i.Type == spec.PIDNamespace { 843 hasPidNs = true 844 break 845 } 846 } 847 } 848 if !hasPidNs { 849 // do not fail on errors 850 _ = c.ociRuntime.KillContainer(c, uint(unix.SIGKILL), true) 851 } 852 853 // Check for running exec sessions 854 sessions, err := c.getActiveExecSessions() 855 if err != nil { 856 return err 857 } 858 if len(sessions) > 0 { 859 return fmt.Errorf("container %s has active exec sessions, refusing to clean up: %w", c.ID(), define.ErrCtrStateInvalid) 860 } 861 862 defer c.newContainerEvent(events.Cleanup) 863 return c.cleanup(ctx) 864 } 865 866 // Batch starts a batch operation on the given container 867 // All commands in the passed function will execute under the same lock and 868 // without synchronizing state after each operation 869 // This will result in substantial performance benefits when running numerous 870 // commands on the same container 871 // Note that the container passed into the Batch function cannot be removed 872 // during batched operations. runtime.RemoveContainer can only be called outside 873 // of Batch 874 // Any error returned by the given batch function will be returned unmodified by 875 // Batch 876 // As Batch normally disables updating the current state of the container, the 877 // Sync() function is provided to enable container state to be updated and 878 // checked within Batch. 879 func (c *Container) Batch(batchFunc func(*Container) error) error { 880 c.lock.Lock() 881 defer c.lock.Unlock() 882 883 newCtr := new(Container) 884 newCtr.config = c.config 885 newCtr.state = c.state 886 newCtr.runtime = c.runtime 887 newCtr.ociRuntime = c.ociRuntime 888 newCtr.lock = c.lock 889 newCtr.valid = true 890 891 newCtr.batched = true 892 err := batchFunc(newCtr) 893 newCtr.batched = false 894 895 return err 896 } 897 898 // Sync updates the status of a container by querying the OCI runtime. 899 // If the container has not been created inside the OCI runtime, nothing will be 900 // done. 901 // Most of the time, Podman does not explicitly query the OCI runtime for 902 // container status, and instead relies upon exit files created by conmon. 903 // This can cause a disconnect between running state and what Podman sees in 904 // cases where Conmon was killed unexpectedly, or runc was upgraded. 905 // Running a manual Sync() ensures that container state will be correct in 906 // such situations. 907 func (c *Container) Sync() error { 908 if !c.batched { 909 c.lock.Lock() 910 defer c.lock.Unlock() 911 } 912 913 if err := c.syncContainer(); err != nil { 914 return err 915 } 916 917 defer c.newContainerEvent(events.Sync) 918 return nil 919 } 920 921 // ReloadNetwork reconfigures the container's network. 922 // Technically speaking, it will tear down and then reconfigure the container's 923 // network namespace, which will result in all firewall rules being recreated. 924 // It is mostly intended to be used in cases where the system firewall has been 925 // reloaded, and existing rules have been wiped out. It is expected that some 926 // downtime will result, as the rules are destroyed as part of this process. 927 // At present, this only works on root containers; it may be expanded to restart 928 // slirp4netns in the future to work with rootless containers as well. 929 // Requires that the container must be running or created. 930 func (c *Container) ReloadNetwork() error { 931 if !c.batched { 932 c.lock.Lock() 933 defer c.lock.Unlock() 934 935 if err := c.syncContainer(); err != nil { 936 return err 937 } 938 } 939 940 if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { 941 return fmt.Errorf("cannot reload network unless container network has been configured: %w", define.ErrCtrStateInvalid) 942 } 943 944 return c.reloadNetwork() 945 } 946 947 // Refresh is DEPRECATED and REMOVED. 948 func (c *Container) Refresh(ctx context.Context) error { 949 // This has been deprecated for a long while, and is in the process of 950 // being removed. 951 return define.ErrNotImplemented 952 } 953 954 // ContainerCheckpointOptions is a struct used to pass the parameters 955 // for checkpointing (and restoring) to the corresponding functions 956 type ContainerCheckpointOptions struct { 957 // Keep tells the API to not delete checkpoint artifacts 958 Keep bool 959 // KeepRunning tells the API to keep the container running 960 // after writing the checkpoint to disk 961 KeepRunning bool 962 // TCPEstablished tells the API to checkpoint a container 963 // even if it contains established TCP connections 964 TCPEstablished bool 965 // TargetFile tells the API to read (or write) the checkpoint image 966 // from (or to) the filename set in TargetFile 967 TargetFile string 968 // CheckpointImageID tells the API to restore the container from 969 // checkpoint image with ID set in CheckpointImageID 970 CheckpointImageID string 971 // Name tells the API that during restore from an exported 972 // checkpoint archive a new name should be used for the 973 // restored container 974 Name string 975 // IgnoreRootfs tells the API to not export changes to 976 // the container's root file-system (or to not import) 977 IgnoreRootfs bool 978 // IgnoreStaticIP tells the API to ignore the IP set 979 // during 'podman run' with '--ip'. This is especially 980 // important to be able to restore a container multiple 981 // times with '--import --name'. 982 IgnoreStaticIP bool 983 // IgnoreStaticMAC tells the API to ignore the MAC set 984 // during 'podman run' with '--mac-address'. This is especially 985 // important to be able to restore a container multiple 986 // times with '--import --name'. 987 IgnoreStaticMAC bool 988 // IgnoreVolumes tells the API to not export or not to import 989 // the content of volumes associated with the container 990 IgnoreVolumes bool 991 // Pre Checkpoint container and leave container running 992 PreCheckPoint bool 993 // Dump container with Pre Checkpoint images 994 WithPrevious bool 995 // ImportPrevious tells the API to restore container with two 996 // images. One is TargetFile, the other is ImportPrevious. 997 ImportPrevious string 998 // CreateImage tells Podman to create an OCI image from container 999 // checkpoint in the local image store. 1000 CreateImage string 1001 // Compression tells the API which compression to use for 1002 // the exported checkpoint archive. 1003 Compression archive.Compression 1004 // If Pod is set the container should be restored into the 1005 // given Pod. If Pod is empty it is a restore without a Pod. 1006 // Restoring a non Pod container into a Pod or a Pod container 1007 // without a Pod is theoretically possible, but will 1008 // probably not work if a PID namespace is shared. 1009 // A shared PID namespace means that a Pod container has PID 1 1010 // in the infrastructure container, but without the infrastructure 1011 // container no PID 1 will be in the namespace and that is not 1012 // possible. 1013 Pod string 1014 // PrintStats tells the API to fill out the statistics about 1015 // how much time each component in the stack requires to 1016 // checkpoint a container. 1017 PrintStats bool 1018 // FileLocks tells the API to checkpoint/restore a container 1019 // with file-locks 1020 FileLocks bool 1021 } 1022 1023 // Checkpoint checkpoints a container 1024 // The return values *define.CRIUCheckpointRestoreStatistics and int64 (time 1025 // the runtime needs to checkpoint the container) are only set if 1026 // options.PrintStats is set to true. Not setting options.PrintStats to true 1027 // will return nil and 0. 1028 func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointOptions) (*define.CRIUCheckpointRestoreStatistics, int64, error) { 1029 logrus.Debugf("Trying to checkpoint container %s", c.ID()) 1030 1031 if options.TargetFile != "" { 1032 if err := c.prepareCheckpointExport(); err != nil { 1033 return nil, 0, err 1034 } 1035 } 1036 1037 if options.WithPrevious { 1038 if err := c.canWithPrevious(); err != nil { 1039 return nil, 0, err 1040 } 1041 } 1042 1043 if !c.batched { 1044 c.lock.Lock() 1045 defer c.lock.Unlock() 1046 1047 if err := c.syncContainer(); err != nil { 1048 return nil, 0, err 1049 } 1050 } 1051 return c.checkpoint(ctx, options) 1052 } 1053 1054 // Restore restores a container 1055 // The return values *define.CRIUCheckpointRestoreStatistics and int64 (time 1056 // the runtime needs to restore the container) are only set if 1057 // options.PrintStats is set to true. Not setting options.PrintStats to true 1058 // will return nil and 0. 1059 func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) (*define.CRIUCheckpointRestoreStatistics, int64, error) { 1060 if options.Pod == "" { 1061 logrus.Debugf("Trying to restore container %s", c.ID()) 1062 } else { 1063 logrus.Debugf("Trying to restore container %s into pod %s", c.ID(), options.Pod) 1064 } 1065 if !c.batched { 1066 c.lock.Lock() 1067 defer c.lock.Unlock() 1068 1069 if err := c.syncContainer(); err != nil { 1070 return nil, 0, err 1071 } 1072 } 1073 defer c.newContainerEvent(events.Restore) 1074 return c.restore(ctx, options) 1075 } 1076 1077 // Indicate whether or not the container should restart 1078 func (c *Container) ShouldRestart(ctx context.Context) bool { 1079 logrus.Debugf("Checking if container %s should restart", c.ID()) 1080 if !c.batched { 1081 c.lock.Lock() 1082 defer c.lock.Unlock() 1083 1084 if err := c.syncContainer(); err != nil { 1085 return false 1086 } 1087 } 1088 return c.shouldRestart() 1089 } 1090 1091 // CopyFromArchive copies the contents from the specified tarStream to path 1092 // *inside* the container. 1093 func (c *Container) CopyFromArchive(_ context.Context, containerPath string, chown, noOverwriteDirNonDir bool, rename map[string]string, tarStream io.Reader) (func() error, error) { 1094 if !c.batched { 1095 c.lock.Lock() 1096 defer c.lock.Unlock() 1097 1098 if err := c.syncContainer(); err != nil { 1099 return nil, err 1100 } 1101 } 1102 1103 return c.copyFromArchive(containerPath, chown, noOverwriteDirNonDir, rename, tarStream) 1104 } 1105 1106 // CopyToArchive copies the contents from the specified path *inside* the 1107 // container to the tarStream. 1108 func (c *Container) CopyToArchive(ctx context.Context, containerPath string, tarStream io.Writer) (func() error, error) { 1109 if !c.batched { 1110 c.lock.Lock() 1111 defer c.lock.Unlock() 1112 1113 if err := c.syncContainer(); err != nil { 1114 return nil, err 1115 } 1116 } 1117 1118 return c.copyToArchive(containerPath, tarStream) 1119 } 1120 1121 // Stat the specified path *inside* the container and return a file info. 1122 func (c *Container) Stat(ctx context.Context, containerPath string) (*define.FileInfo, error) { 1123 if !c.batched { 1124 c.lock.Lock() 1125 defer c.lock.Unlock() 1126 1127 if err := c.syncContainer(); err != nil { 1128 return nil, err 1129 } 1130 } 1131 1132 var mountPoint string 1133 var err error 1134 if c.state.Mounted { 1135 mountPoint = c.state.Mountpoint 1136 } else { 1137 mountPoint, err = c.mount() 1138 if err != nil { 1139 return nil, err 1140 } 1141 defer func() { 1142 if err := c.unmount(false); err != nil { 1143 logrus.Errorf("Unmounting container %s: %v", c.ID(), err) 1144 } 1145 }() 1146 } 1147 1148 info, _, _, err := c.stat(mountPoint, containerPath) 1149 return info, err 1150 } 1151 1152 func saveContainerError(c *Container, err error) error { 1153 c.state.Error = err.Error() 1154 return c.save() 1155 }