podman
1//go:build !remote
2
3package libpod4
5import (6"bytes"7"errors"8"fmt"9"io"10"net"11"os"12"strings"13"time"14
15"github.com/containers/common/libnetwork/pasta"16"github.com/containers/common/libnetwork/types"17"github.com/containers/common/pkg/config"18"github.com/containers/common/pkg/secrets"19"github.com/containers/image/v5/manifest"20"github.com/containers/podman/v5/libpod/define"21"github.com/containers/podman/v5/libpod/lock"22"github.com/containers/storage"23spec "github.com/opencontainers/runtime-spec/specs-go"24"github.com/sirupsen/logrus"25"golang.org/x/sys/unix"26)
27
28// CgroupfsDefaultCgroupParent is the cgroup parent for CgroupFS in libpod
29const CgroupfsDefaultCgroupParent = "/libpod_parent"30
31// SystemdDefaultCgroupParent is the cgroup parent for the systemd cgroup
32// manager in libpod
33const SystemdDefaultCgroupParent = "machine.slice"34
35// SystemdDefaultRootlessCgroupParent is the cgroup parent for the systemd cgroup
36// manager in libpod when running as rootless
37const SystemdDefaultRootlessCgroupParent = "user.slice"38
39// DefaultWaitInterval is the default interval between container status checks
40// while waiting.
41const DefaultWaitInterval = 250 * time.Millisecond42
43// LinuxNS represents a Linux namespace
44type LinuxNS int45
46const (47// InvalidNS is an invalid namespace48InvalidNS LinuxNS = iota49// IPCNS is the IPC namespace50IPCNS LinuxNS = iota51// MountNS is the mount namespace52MountNS LinuxNS = iota53// NetNS is the network namespace54NetNS LinuxNS = iota55// PIDNS is the PID namespace56PIDNS LinuxNS = iota57// UserNS is the user namespace58UserNS LinuxNS = iota59// UTSNS is the UTS namespace60UTSNS LinuxNS = iota61// CgroupNS is the Cgroup namespace62CgroupNS LinuxNS = iota63)
64
65// String returns a string representation of a Linux namespace
66// It is guaranteed to be the name of the namespace in /proc for valid ns types
67func (ns LinuxNS) String() string {68switch ns {69case InvalidNS:70return "invalid"71case IPCNS:72return "ipc"73case MountNS:74return "mnt"75case NetNS:76return "net"77case PIDNS:78return "pid"79case UserNS:80return "user"81case UTSNS:82return "uts"83case CgroupNS:84return "cgroup"85default:86return "unknown"87}88}
89
90// Container is a single OCI container.
91// All operations on a Container that access state must begin with a call to
92// syncContainer().
93// There is no guarantee that state exists in a readable state before
94// syncContainer() is run, and even if it does, its contents will be out of date
95// and must be refreshed from the database.
96// Generally, this requirement applies only to top-level functions; helpers can
97// assume that their callers handled this requirement. Generally speaking, if a
98// function takes the container lock and accesses any part of state, it should
99// syncContainer() immediately after locking.
100type Container struct {101config *ContainerConfig102
103state *ContainerState104
105// Batched indicates that a container has been locked as part of a106// Batch() operation107// Functions called on a batched container will not lock or sync108batched bool109
110valid bool111lock lock.Locker112runtime *Runtime113ociRuntime OCIRuntime
114
115rootlessSlirpSyncR *os.File116rootlessSlirpSyncW *os.File117
118rootlessPortSyncR *os.File119rootlessPortSyncW *os.File120
121// perNetworkOpts should be set when you want to use special network122// options when calling network setup/teardown. This should be used for123// container restore or network reload for example. Leave this nil if124// the settings from the container config should be used.125perNetworkOpts map[string]types.PerNetworkOptions126
127// This is true if a container is restored from a checkpoint.128restoreFromCheckpoint bool129
130slirp4netnsSubnet *net.IPNet131pastaResult *pasta.SetupResult132}
133
134// ContainerState contains the current state of the container
135// It is stored on disk in a tmpfs and recreated on reboot
136type ContainerState struct {137// The current state of the running container138State define.ContainerStatus `json:"state"`139// The path to the JSON OCI runtime spec for this container140ConfigPath string `json:"configPath,omitempty"`141// RunDir is a per-boot directory for container content142RunDir string `json:"runDir,omitempty"`143// Mounted indicates whether the container's storage has been mounted144// for use145Mounted bool `json:"mounted,omitempty"`146// Mountpoint contains the path to the container's mounted storage as given147// by containers/storage.148Mountpoint string `json:"mountPoint,omitempty"`149// StartedTime is the time the container was started150StartedTime time.Time `json:"startedTime,omitempty"`151// FinishedTime is the time the container finished executing152FinishedTime time.Time `json:"finishedTime,omitempty"`153// ExitCode is the exit code returned when the container stopped154ExitCode int32 `json:"exitCode,omitempty"`155// Exited is whether the container has exited156Exited bool `json:"exited,omitempty"`157// Error holds the last known error message during start, stop, or remove158Error string `json:"error,omitempty"`159// OOMKilled indicates that the container was killed as it ran out of160// memory161OOMKilled bool `json:"oomKilled,omitempty"`162// Checkpointed indicates that the container was stopped by a checkpoint163// operation.164Checkpointed bool `json:"checkpointed,omitempty"`165// PID is the PID of a running container166PID int `json:"pid,omitempty"`167// ConmonPID is the PID of the container's conmon168ConmonPID int `json:"conmonPid,omitempty"`169// ExecSessions contains all exec sessions that are associated with this170// container.171ExecSessions map[string]*ExecSession `json:"newExecSessions,omitempty"`172// LegacyExecSessions are legacy exec sessions from older versions of173// Podman.174// These are DEPRECATED and will be removed in a future release.175LegacyExecSessions map[string]*legacyExecSession `json:"execSessions,omitempty"`176// NetNS is the path or name of the NetNS177NetNS string `json:"netns,omitempty"`178// NetworkStatus contains the network Status for all networks179// the container is attached to. Only populated if we created a network180// namespace for the container, and the network namespace is currently181// active.182// To read this field use container.getNetworkStatus() instead, this will183// take care of migrating the old DEPRECATED network status to the new format.184NetworkStatus map[string]types.StatusBlock `json:"networkStatus,omitempty"`185// BindMounts contains files that will be bind-mounted into the186// container when it is mounted.187// These include /etc/hosts and /etc/resolv.conf188// This maps the path the file will be mounted to in the container to189// the path of the file on disk outside the container190BindMounts map[string]string `json:"bindMounts,omitempty"`191// StoppedByUser indicates whether the container was stopped by an192// explicit call to the Stop() API.193StoppedByUser bool `json:"stoppedByUser,omitempty"`194// RestartPolicyMatch indicates whether the conditions for restart195// policy have been met.196RestartPolicyMatch bool `json:"restartPolicyMatch,omitempty"`197// RestartCount is how many times the container was restarted by its198// restart policy. This is NOT incremented by normal container restarts199// (only by restart policy).200RestartCount uint `json:"restartCount,omitempty"`201// StartupHCPassed indicates that the startup healthcheck has202// succeeded and the main healthcheck can begin.203StartupHCPassed bool `json:"startupHCPassed,omitempty"`204// StartupHCSuccessCount indicates the number of successes of the205// startup healthcheck. A startup HC can require more than one success206// to be marked as passed.207StartupHCSuccessCount int `json:"startupHCSuccessCount,omitempty"`208// StartupHCFailureCount indicates the number of failures of the startup209// healthcheck. The container will be restarted if this exceed a set210// number in the startup HC config.211StartupHCFailureCount int `json:"startupHCFailureCount,omitempty"`212
213// ExtensionStageHooks holds hooks which will be executed by libpod214// and not delegated to the OCI runtime.215ExtensionStageHooks map[string][]spec.Hook `json:"extensionStageHooks,omitempty"`216
217// NetInterfaceDescriptions describe the relationship between a CNI218// network and an interface names219NetInterfaceDescriptions ContainerNetworkDescriptions `json:"networkDescriptions,omitempty"`220
221// Service indicates that container is the service container of a222// service. A service consists of one or more pods. The service223// container is started before all pods and is stopped when the last224// pod stops. The service container allows for tracking and managing225// the entire life cycle of service which may be started via226// `podman-play-kube`.227Service Service
228
229// Following checkpoint/restore related information is displayed230// if the container has been checkpointed or restored.231CheckpointedTime time.Time `json:"checkpointedTime,omitempty"`232RestoredTime time.Time `json:"restoredTime,omitempty"`233CheckpointLog string `json:"checkpointLog,omitempty"`234CheckpointPath string `json:"checkpointPath,omitempty"`235RestoreLog string `json:"restoreLog,omitempty"`236Restored bool `json:"restored,omitempty"`237}
238
239// ContainerNamedVolume is a named volume that will be mounted into the
240// container. Each named volume is a libpod Volume present in the state.
241type ContainerNamedVolume struct {242// Name is the name of the volume to mount in.243// Must resolve to a valid volume present in this Podman.244Name string `json:"volumeName"`245// Dest is the mount's destination246Dest string `json:"dest"`247// Options are fstab style mount options248Options []string `json:"options,omitempty"`249// IsAnonymous sets the named volume as anonymous even if it has a name250// This is used for emptyDir volumes from a kube yaml251IsAnonymous bool `json:"setAnonymous,omitempty"`252// SubPath determines which part of the Source will be mounted in the container253SubPath string254}
255
256// ContainerOverlayVolume is an overlay volume that will be mounted into the
257// container. Each volume is a libpod Volume present in the state.
258type ContainerOverlayVolume struct {259// Destination is the absolute path where the mount will be placed in the container.260Dest string `json:"dest"`261// Source specifies the source path of the mount.262Source string `json:"source,omitempty"`263// Options holds overlay volume options.264Options []string `json:"options,omitempty"`265}
266
267// ContainerImageVolume is a volume based on a container image. The container
268// image is first mounted on the host and is then bind-mounted into the
269// container.
270type ContainerImageVolume struct {271// Source is the source of the image volume. The image can be referred272// to by name and by ID.273Source string `json:"source"`274// Dest is the absolute path of the mount in the container.275Dest string `json:"dest"`276// ReadWrite sets the volume writable.277ReadWrite bool `json:"rw"`278}
279
280// ContainerSecret is a secret that is mounted in a container
281type ContainerSecret struct {282// Secret is the secret283*secrets.Secret284// UID is the UID of the secret file285UID uint32286// GID is the GID of the secret file287GID uint32288// Mode is the mode of the secret file289Mode uint32290// Secret target inside container291Target string292}
293
294// ContainerNetworkDescriptions describes the relationship between the CNI
295// network and the ethN where N is an integer
296type ContainerNetworkDescriptions map[string]int297
298// Config accessors
299// Unlocked
300
301// Config returns the configuration used to create the container.
302// Note that the returned config does not include the actual networks.
303// Use ConfigWithNetworks() if you need them.
304func (c *Container) Config() *ContainerConfig {305returnConfig := new(ContainerConfig)306if err := JSONDeepCopy(c.config, returnConfig); err != nil {307return nil308}309return returnConfig310}
311
312// Config returns the configuration used to create the container.
313func (c *Container) ConfigWithNetworks() *ContainerConfig {314returnConfig := c.Config()315if returnConfig == nil {316return nil317}318
319networks, err := c.networks()320if err != nil {321return nil322}323returnConfig.Networks = networks324
325return returnConfig326}
327
328// ConfigNoCopy returns the configuration used by the container.
329// Note that the returned value is not a copy and must hence
330// only be used in a reading fashion.
331func (c *Container) ConfigNoCopy() *ContainerConfig {332return c.config333}
334
335// DeviceHostSrc returns the user supplied device to be passed down in the pod
336func (c *Container) DeviceHostSrc() []spec.LinuxDevice {337return c.config.DeviceHostSrc338}
339
340// Runtime returns the container's Runtime.
341func (c *Container) Runtime() *Runtime {342return c.runtime343}
344
345// Spec returns the container's OCI runtime spec
346// The spec returned is the one used to create the container. The running
347// spec may differ slightly as mounts are added based on the image
348func (c *Container) Spec() *spec.Spec {349returnSpec := new(spec.Spec)350if err := JSONDeepCopy(c.config.Spec, returnSpec); err != nil {351return nil352}353
354return returnSpec355}
356
357// specFromState returns the unmarshalled json config of the container. If the
358// config does not exist (e.g., because the container was never started) return
359// the spec from the config.
360func (c *Container) specFromState() (*spec.Spec, error) {361returnSpec := c.config.Spec362
363if f, err := os.Open(c.state.ConfigPath); err == nil {364returnSpec = new(spec.Spec)365content, err := io.ReadAll(f)366if err != nil {367return nil, fmt.Errorf("reading container config: %w", err)368}369if err := json.Unmarshal(content, &returnSpec); err != nil {370// Malformed spec, just use c.config.Spec instead371logrus.Warnf("Error unmarshalling container %s config: %v", c.ID(), err)372return c.config.Spec, nil373}374} else if !os.IsNotExist(err) {375// ignore when the file does not exist376return nil, fmt.Errorf("opening container config: %w", err)377}378
379return returnSpec, nil380}
381
382// ID returns the container's ID
383func (c *Container) ID() string {384return c.config.ID385}
386
387// Name returns the container's name
388func (c *Container) Name() string {389return c.config.Name390}
391
392// PodID returns the full ID of the pod the container belongs to, or "" if it
393// does not belong to a pod
394func (c *Container) PodID() string {395return c.config.Pod396}
397
398// Namespace returns the libpod namespace the container is in.
399// Namespaces are used to logically separate containers and pods in the state.
400func (c *Container) Namespace() string {401return c.config.Namespace402}
403
404// Image returns the ID and name of the image used as the container's rootfs.
405func (c *Container) Image() (string, string) {406return c.config.RootfsImageID, c.config.RootfsImageName407}
408
409// RawImageName returns the unprocessed and not-normalized user-specified image
410// name.
411func (c *Container) RawImageName() string {412return c.config.RawImageName413}
414
415// ShmDir returns the sources path to be mounted on /dev/shm in container
416func (c *Container) ShmDir() string {417return c.config.ShmDir418}
419
420// ShmSize returns the size of SHM device to be mounted into the container
421func (c *Container) ShmSize() int64 {422return c.config.ShmSize423}
424
425// StaticDir returns the directory used to store persistent container files
426func (c *Container) StaticDir() string {427return c.config.StaticDir428}
429
430// NamedVolumes returns the container's named volumes.
431// The name of each is guaranteed to point to a valid libpod Volume present in
432// the state.
433func (c *Container) NamedVolumes() []*ContainerNamedVolume {434volumes := []*ContainerNamedVolume{}435for _, vol := range c.config.NamedVolumes {436newVol := new(ContainerNamedVolume)437newVol.Name = vol.Name438newVol.Dest = vol.Dest439newVol.Options = vol.Options440newVol.SubPath = vol.SubPath441volumes = append(volumes, newVol)442}443
444return volumes445}
446
447// Privileged returns whether the container is privileged
448func (c *Container) Privileged() bool {449return c.config.Privileged450}
451
452// ProcessLabel returns the selinux ProcessLabel of the container
453func (c *Container) ProcessLabel() string {454return c.config.ProcessLabel455}
456
457// MountLabel returns the SELinux mount label of the container
458func (c *Container) MountLabel() string {459return c.config.MountLabel460}
461
462// Systemd returns whether the container will be running in systemd mode
463func (c *Container) Systemd() bool {464if c.config.Systemd != nil {465return *c.config.Systemd466}467return false468}
469
470// User returns the user who the container is run as
471func (c *Container) User() string {472return c.config.User473}
474
475// Dependencies gets the containers this container depends upon
476func (c *Container) Dependencies() []string {477// Collect in a map first to remove dupes478dependsCtrs := map[string]bool{}479
480// First add all namespace containers481if c.config.IPCNsCtr != "" {482dependsCtrs[c.config.IPCNsCtr] = true483}484if c.config.MountNsCtr != "" {485dependsCtrs[c.config.MountNsCtr] = true486}487if c.config.NetNsCtr != "" {488dependsCtrs[c.config.NetNsCtr] = true489}490if c.config.PIDNsCtr != "" {491dependsCtrs[c.config.PIDNsCtr] = true492}493if c.config.UserNsCtr != "" {494dependsCtrs[c.config.UserNsCtr] = true495}496if c.config.UTSNsCtr != "" {497dependsCtrs[c.config.UTSNsCtr] = true498}499if c.config.CgroupNsCtr != "" {500dependsCtrs[c.config.CgroupNsCtr] = true501}502
503// Add all generic dependencies504for _, id := range c.config.Dependencies {505dependsCtrs[id] = true506}507
508if len(dependsCtrs) == 0 {509return []string{}510}511
512depends := make([]string, 0, len(dependsCtrs))513for ctr := range dependsCtrs {514depends = append(depends, ctr)515}516
517return depends518}
519
520// NewNetNS returns whether the container will create a new network namespace
521func (c *Container) NewNetNS() bool {522return c.config.CreateNetNS523}
524
525// PortMappings returns the ports that will be mapped into a container if
526// a new network namespace is created
527// If NewNetNS() is false, this value is unused
528func (c *Container) PortMappings() ([]types.PortMapping, error) {529// First check if the container belongs to a network namespace (like a pod)530if len(c.config.NetNsCtr) > 0 {531netNsCtr, err := c.runtime.GetContainer(c.config.NetNsCtr)532if err != nil {533return nil, fmt.Errorf("unable to look up network namespace for container %s: %w", c.ID(), err)534}535return netNsCtr.PortMappings()536}537return c.config.PortMappings, nil538}
539
540// DNSServers returns DNS servers that will be used in the container's
541// resolv.conf
542// If empty, DNS server from the host's resolv.conf will be used instead
543func (c *Container) DNSServers() []net.IP {544return c.config.DNSServer545}
546
547// DNSSearch returns the DNS search domains that will be used in the container's
548// resolv.conf
549// If empty, DNS Search domains from the host's resolv.conf will be used instead
550func (c *Container) DNSSearch() []string {551return c.config.DNSSearch552}
553
554// DNSOption returns the DNS options that will be used in the container's
555// resolv.conf
556// If empty, options from the host's resolv.conf will be used instead
557func (c *Container) DNSOption() []string {558return c.config.DNSOption559}
560
561// HostsAdd returns hosts that will be added to the container's hosts file
562// The host system's hosts file is used as a base, and these are appended to it
563func (c *Container) HostsAdd() []string {564return c.config.HostAdd565}
566
567// UserVolumes returns user-added volume mounts in the container.
568// These are not added to the spec, but are used during image commit and to
569// trigger some OCI hooks.
570func (c *Container) UserVolumes() []string {571volumes := make([]string, 0, len(c.config.UserVolumes))572volumes = append(volumes, c.config.UserVolumes...)573return volumes574}
575
576// Entrypoint is the container's entrypoint.
577// This is not added to the spec, but is instead used during image commit.
578func (c *Container) Entrypoint() []string {579entrypoint := make([]string, 0, len(c.config.Entrypoint))580entrypoint = append(entrypoint, c.config.Entrypoint...)581return entrypoint582}
583
584// Command is the container's command
585// This is not added to the spec, but is instead used during image commit
586func (c *Container) Command() []string {587command := make([]string, 0, len(c.config.Command))588command = append(command, c.config.Command...)589return command590}
591
592// Stdin returns whether STDIN on the container will be kept open
593func (c *Container) Stdin() bool {594return c.config.Stdin595}
596
597// Labels returns the container's labels
598func (c *Container) Labels() map[string]string {599labels := make(map[string]string)600for key, value := range c.config.Labels {601labels[key] = value602}603return labels604}
605
606// StopSignal is the signal that will be used to stop the container
607// If it fails to stop the container, SIGKILL will be used after a timeout
608// If StopSignal is 0, the default signal of SIGTERM will be used
609func (c *Container) StopSignal() uint {610return c.config.StopSignal611}
612
613// StopTimeout returns the container's stop timeout
614// If the container's default stop signal fails to kill the container, SIGKILL
615// will be used after this timeout
616func (c *Container) StopTimeout() uint {617return c.config.StopTimeout618}
619
620// CreatedTime gets the time when the container was created
621func (c *Container) CreatedTime() time.Time {622return c.config.CreatedTime623}
624
625// CgroupParent gets the container's Cgroup parent
626func (c *Container) CgroupParent() string {627return c.config.CgroupParent628}
629
630// LogPath returns the path to the container's log file
631// This file will only be present after Init() is called to create the container
632// in the runtime
633func (c *Container) LogPath() string {634return c.config.LogPath635}
636
637// LogTag returns the tag to the container's log file
638func (c *Container) LogTag() string {639return c.config.LogTag640}
641
642// RestartPolicy returns the container's restart policy.
643func (c *Container) RestartPolicy() string {644return c.config.RestartPolicy645}
646
647// RestartRetries returns the number of retries that will be attempted when
648// using the "on-failure" restart policy
649func (c *Container) RestartRetries() uint {650return c.config.RestartRetries651}
652
653// LogDriver returns the log driver for this container
654func (c *Container) LogDriver() string {655return c.config.LogDriver656}
657
658// RuntimeName returns the name of the runtime
659func (c *Container) RuntimeName() string {660return c.config.OCIRuntime661}
662
663// Runtime spec accessors
664// Unlocked
665
666// Hostname gets the container's hostname
667func (c *Container) Hostname() string {668if c.config.UTSNsCtr != "" {669utsNsCtr, err := c.runtime.GetContainer(c.config.UTSNsCtr)670if err != nil {671// should we return an error here?672logrus.Errorf("unable to look up uts namespace for container %s: %v", c.ID(), err)673return ""674}675return utsNsCtr.Hostname()676}677if c.config.Spec.Hostname != "" {678return c.config.Spec.Hostname679}680
681// if the container is not running in a private UTS namespace,682// return the host's hostname.683privateUTS := c.hasPrivateUTS()684if !privateUTS {685hostname, err := os.Hostname()686if err == nil {687return hostname688}689}690if len(c.ID()) < 11 {691return c.ID()692}693return c.ID()[:12]694}
695
696// WorkingDir returns the containers working dir
697func (c *Container) WorkingDir() string {698if c.config.Spec.Process != nil {699return c.config.Spec.Process.Cwd700}701return "/"702}
703
704// Terminal returns true if the container has a terminal
705func (c *Container) Terminal() bool {706if c.config.Spec != nil && c.config.Spec.Process != nil {707return c.config.Spec.Process.Terminal708}709return false710}
711
712// LinuxResources return the containers Linux Resources (if any)
713func (c *Container) LinuxResources() *spec.LinuxResources {714if c.config.Spec != nil && c.config.Spec.Linux != nil {715return c.config.Spec.Linux.Resources716}717return nil718}
719
720// Env returns the default environment variables defined for the container
721func (c *Container) Env() []string {722if c.config.Spec != nil && c.config.Spec.Process != nil {723return c.config.Spec.Process.Env724}725return nil726}
727
728// State Accessors
729// Require locking
730
731// State returns the current state of the container
732func (c *Container) State() (define.ContainerStatus, error) {733if !c.batched {734c.lock.Lock()735defer c.lock.Unlock()736
737if err := c.syncContainer(); err != nil {738return define.ContainerStateUnknown, err739}740}741return c.state.State, nil742}
743
744func (c *Container) RestartCount() (uint, error) {745if !c.batched {746c.lock.Lock()747defer c.lock.Unlock()748
749if err := c.syncContainer(); err != nil {750return 0, err751}752}753return c.state.RestartCount, nil754}
755
756// Mounted returns whether the container is mounted and the path it is mounted
757// at (if it is mounted).
758// If the container is not mounted, no error is returned, and the mountpoint
759// will be set to "".
760func (c *Container) Mounted() (bool, string, error) {761if !c.batched {762c.lock.Lock()763defer c.lock.Unlock()764if err := c.syncContainer(); err != nil {765return false, "", fmt.Errorf("updating container %s state: %w", c.ID(), err)766}767}768// We cannot directly return c.state.Mountpoint as it is not guaranteed769// to be set if the container is mounted, only if the container has been770// prepared with c.prepare().771// Instead, let's call into c/storage772mountedTimes, err := c.runtime.storageService.MountedContainerImage(c.ID())773if err != nil {774return false, "", err775}776
777if mountedTimes > 0 {778mountPoint, err := c.runtime.storageService.GetMountpoint(c.ID())779if err != nil {780return false, "", err781}782
783return true, mountPoint, nil784}785
786return false, "", nil787}
788
789// StartedTime is the time the container was started
790func (c *Container) StartedTime() (time.Time, error) {791if !c.batched {792c.lock.Lock()793defer c.lock.Unlock()794if err := c.syncContainer(); err != nil {795return time.Time{}, fmt.Errorf("updating container %s state: %w", c.ID(), err)796}797}798return c.state.StartedTime, nil799}
800
801// FinishedTime is the time the container was stopped
802func (c *Container) FinishedTime() (time.Time, error) {803if !c.batched {804c.lock.Lock()805defer c.lock.Unlock()806if err := c.syncContainer(); err != nil {807return time.Time{}, fmt.Errorf("updating container %s state: %w", c.ID(), err)808}809}810return c.state.FinishedTime, nil811}
812
813// ExitCode returns the exit code of the container as
814// an int32, and whether the container has exited.
815// If the container has not exited, exit code will always be 0.
816// If the container restarts, the exit code is reset to 0.
817func (c *Container) ExitCode() (int32, bool, error) {818if !c.batched {819c.lock.Lock()820defer c.lock.Unlock()821if err := c.syncContainer(); err != nil {822return 0, false, fmt.Errorf("updating container %s state: %w", c.ID(), err)823}824}825return c.state.ExitCode, c.state.Exited, nil826}
827
828// OOMKilled returns whether the container was killed by an OOM condition
829func (c *Container) OOMKilled() (bool, error) {830if !c.batched {831c.lock.Lock()832defer c.lock.Unlock()833if err := c.syncContainer(); err != nil {834return false, fmt.Errorf("updating container %s state: %w", c.ID(), err)835}836}837return c.state.OOMKilled, nil838}
839
840// PID returns the PID of the container.
841// If the container is not running, a pid of 0 will be returned. No error will
842// occur.
843func (c *Container) PID() (int, error) {844if !c.batched {845c.lock.Lock()846defer c.lock.Unlock()847
848if err := c.syncContainer(); err != nil {849return -1, err850}851}852
853return c.state.PID, nil854}
855
856// ConmonPID Returns the PID of the container's conmon process.
857// If the container is not running, a PID of 0 will be returned. No error will
858// occur.
859func (c *Container) ConmonPID() (int, error) {860if !c.batched {861c.lock.Lock()862defer c.lock.Unlock()863
864if err := c.syncContainer(); err != nil {865return -1, err866}867}868
869return c.state.ConmonPID, nil870}
871
872// ExecSessions retrieves active exec sessions running in the container
873func (c *Container) ExecSessions() ([]string, error) {874if !c.batched {875c.lock.Lock()876defer c.lock.Unlock()877
878if err := c.syncContainer(); err != nil {879return nil, err880}881}882
883ids := make([]string, 0, len(c.state.ExecSessions))884for id := range c.state.ExecSessions {885ids = append(ids, id)886}887
888return ids, nil889}
890
891// execSessionNoCopy returns the associated exec session to id.
892// Note that the session is not a deep copy.
893func (c *Container) execSessionNoCopy(id string) (*ExecSession, error) {894if !c.batched {895c.lock.Lock()896defer c.lock.Unlock()897
898if err := c.syncContainer(); err != nil {899return nil, err900}901}902
903session, ok := c.state.ExecSessions[id]904if !ok {905return nil, fmt.Errorf("no exec session with ID %s found in container %s: %w", id, c.ID(), define.ErrNoSuchExecSession)906}907
908// make sure to update the exec session if needed #18424909alive, err := c.ociRuntime.ExecUpdateStatus(c, id)910if err != nil {911return nil, err912}913if !alive {914if err := retrieveAndWriteExecExitCode(c, session.ID()); err != nil {915return nil, err916}917}918
919return session, nil920}
921
922// ExecSession retrieves detailed information on a single active exec session in
923// a container
924func (c *Container) ExecSession(id string) (*ExecSession, error) {925session, err := c.execSessionNoCopy(id)926if err != nil {927return nil, err928}929
930returnSession := new(ExecSession)931if err := JSONDeepCopy(session, returnSession); err != nil {932return nil, fmt.Errorf("copying contents of container %s exec session %s: %w", c.ID(), session.ID(), err)933}934
935return returnSession, nil936}
937
938// BindMounts retrieves bind mounts that were created by libpod and will be
939// added to the container
940// All these mounts except /dev/shm are ignored if a mount in the given spec has
941// the same destination
942// These mounts include /etc/resolv.conf, /etc/hosts, and /etc/hostname
943// The return is formatted as a map from destination (mountpoint in the
944// container) to source (path of the file that will be mounted into the
945// container)
946// If the container has not been started yet, an empty map will be returned, as
947// the files in question are only created when the container is started.
948func (c *Container) BindMounts() (map[string]string, error) {949if !c.batched {950c.lock.Lock()951defer c.lock.Unlock()952
953if err := c.syncContainer(); err != nil {954return nil, err955}956}957
958newMap := make(map[string]string, len(c.state.BindMounts))959
960for key, val := range c.state.BindMounts {961newMap[key] = val962}963
964return newMap, nil965}
966
967// StoppedByUser returns whether the container was last stopped by an explicit
968// call to the Stop() API, or whether it exited naturally.
969func (c *Container) StoppedByUser() (bool, error) {970if !c.batched {971c.lock.Lock()972defer c.lock.Unlock()973
974if err := c.syncContainer(); err != nil {975return false, err976}977}978
979return c.state.StoppedByUser, nil980}
981
982// StartupHCPassed returns whether the container's startup healthcheck passed.
983func (c *Container) StartupHCPassed() (bool, error) {984if !c.batched {985c.lock.Lock()986defer c.lock.Unlock()987
988if err := c.syncContainer(); err != nil {989return false, err990}991}992
993return c.state.StartupHCPassed, nil994}
995
996// Misc Accessors
997// Most will require locking
998
999// NamespacePath returns the path of one of the container's namespaces
1000// If the container is not running, an error will be returned
1001func (c *Container) NamespacePath(linuxNS LinuxNS) (string, error) { //nolint:interfacer1002if !c.batched {1003c.lock.Lock()1004defer c.lock.Unlock()1005if err := c.syncContainer(); err != nil {1006return "", fmt.Errorf("updating container %s state: %w", c.ID(), err)1007}1008}1009
1010return c.namespacePath(linuxNS)1011}
1012
1013// namespacePath returns the path of one of the container's namespaces
1014// If the container is not running, an error will be returned
1015func (c *Container) namespacePath(linuxNS LinuxNS) (string, error) { //nolint:interfacer1016if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused {1017return "", fmt.Errorf("cannot get namespace path unless container %s is running: %w", c.ID(), define.ErrCtrStopped)1018}1019
1020if linuxNS == InvalidNS {1021return "", fmt.Errorf("invalid namespace requested from container %s: %w", c.ID(), define.ErrInvalidArg)1022}1023
1024return fmt.Sprintf("/proc/%d/ns/%s", c.state.PID, linuxNS.String()), nil1025}
1026
1027// CgroupManager returns the cgroup manager used by the given container.
1028func (c *Container) CgroupManager() string {1029cgroupManager := c.config.CgroupManager1030if cgroupManager == "" {1031cgroupManager = c.runtime.config.Engine.CgroupManager1032}1033return cgroupManager1034}
1035
1036// CgroupPath returns a cgroups "path" for the given container.
1037// Note that the container must be running. Otherwise, an error
1038// is returned.
1039func (c *Container) CgroupPath() (string, error) {1040if !c.batched {1041c.lock.Lock()1042defer c.lock.Unlock()1043if err := c.syncContainer(); err != nil {1044return "", fmt.Errorf("updating container %s state: %w", c.ID(), err)1045}1046}1047return c.cGroupPath()1048}
1049
1050// cGroupPath returns a cgroups "path" for the given container.
1051// Note that the container must be running. Otherwise, an error
1052// is returned.
1053// NOTE: only call this when owning the container's lock.
1054func (c *Container) cGroupPath() (string, error) {1055if c.config.NoCgroups || c.config.CgroupsMode == "disabled" {1056return "", fmt.Errorf("this container is not creating cgroups: %w", define.ErrNoCgroups)1057}1058if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused {1059return "", fmt.Errorf("cannot get cgroup path unless container %s is running: %w", c.ID(), define.ErrCtrStopped)1060}1061
1062// Read /proc/{PID}/cgroup and find the *longest* cgroup entry. That's1063// needed to account for hacks in cgroups v1, where each line in the1064// file could potentially point to a cgroup. The longest one, however,1065// is the libpod-specific one we're looking for.1066//1067// See #8397 on the need for the longest-path look up.1068//1069// And another workaround for containers running systemd as the payload.1070// containers running systemd moves themselves into a child subgroup of1071// the named systemd cgroup hierarchy. Ignore any named cgroups during1072// the lookup.1073// See #10602 for more details.1074procPath := fmt.Sprintf("/proc/%d/cgroup", c.state.PID)1075lines, err := os.ReadFile(procPath)1076if err != nil {1077// If the file doesn't exist, it means the container could have been terminated1078// so report it. Also check for ESRCH, which means the container could have been1079// terminated after the file under /proc was opened but before it was read.1080if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.ESRCH) {1081return "", fmt.Errorf("cannot get cgroup path unless container %s is running: %w", c.ID(), define.ErrCtrStopped)1082}1083return "", err1084}1085
1086var cgroupPath string1087for _, line := range bytes.Split(lines, []byte("\n")) {1088// skip last empty line1089if len(line) == 0 {1090continue1091}1092// cgroups(7) nails it down to three fields with the 3rd1093// pointing to the cgroup's path which works both on v1 and v2.1094fields := bytes.Split(line, []byte(":"))1095if len(fields) != 3 {1096logrus.Debugf("Error parsing cgroup: expected 3 fields but got %d: %s", len(fields), procPath)1097continue1098}1099// Ignore named cgroups like name=systemd.1100if bytes.Contains(fields[1], []byte("=")) {1101continue1102}1103path := string(fields[2])1104if len(path) > len(cgroupPath) {1105cgroupPath = path1106}1107}1108
1109if len(cgroupPath) == 0 {1110return "", fmt.Errorf("could not find any cgroup in %q", procPath)1111}1112
1113cgroupManager := c.CgroupManager()1114switch {1115case c.config.CgroupsMode == cgroupSplit:1116name := fmt.Sprintf("/libpod-payload-%s/", c.ID())1117if index := strings.LastIndex(cgroupPath, name); index >= 0 {1118return cgroupPath[:index+len(name)-1], nil1119}1120case cgroupManager == config.CgroupfsCgroupsManager:1121name := fmt.Sprintf("/libpod-%s/", c.ID())1122if index := strings.LastIndex(cgroupPath, name); index >= 0 {1123return cgroupPath[:index+len(name)-1], nil1124}1125case cgroupManager == config.SystemdCgroupsManager:1126// When running under systemd, try to detect the scope that was requested1127// to be created. It improves the heuristic since we report the first1128// cgroup that was created instead of the cgroup where PID 1 might have1129// moved to.1130name := fmt.Sprintf("/libpod-%s.scope/", c.ID())1131if index := strings.LastIndex(cgroupPath, name); index >= 0 {1132return cgroupPath[:index+len(name)-1], nil1133}1134}1135
1136return cgroupPath, nil1137}
1138
1139// RootFsSize returns the root FS size of the container
1140func (c *Container) RootFsSize() (int64, error) {1141if !c.batched {1142c.lock.Lock()1143defer c.lock.Unlock()1144if err := c.syncContainer(); err != nil {1145return -1, fmt.Errorf("updating container %s state: %w", c.ID(), err)1146}1147}1148return c.rootFsSize()1149}
1150
1151// RWSize returns the rw size of the container
1152func (c *Container) RWSize() (int64, error) {1153if !c.batched {1154c.lock.Lock()1155defer c.lock.Unlock()1156if err := c.syncContainer(); err != nil {1157return -1, fmt.Errorf("updating container %s state: %w", c.ID(), err)1158}1159}1160return c.rwSize()1161}
1162
1163// IDMappings returns the UID/GID mapping used for the container
1164func (c *Container) IDMappings() storage.IDMappingOptions {1165return c.config.IDMappings1166}
1167
1168// RootUID returns the root user mapping from container
1169func (c *Container) RootUID() int {1170if len(c.config.IDMappings.UIDMap) == 1 && c.config.IDMappings.UIDMap[0].Size == 1 {1171return c.config.IDMappings.UIDMap[0].HostID1172}1173for _, uidmap := range c.config.IDMappings.UIDMap {1174if uidmap.ContainerID == 0 {1175return uidmap.HostID1176}1177}1178return 01179}
1180
1181// RootGID returns the root user mapping from container
1182func (c *Container) RootGID() int {1183if len(c.config.IDMappings.GIDMap) == 1 && c.config.IDMappings.GIDMap[0].Size == 1 {1184return c.config.IDMappings.GIDMap[0].HostID1185}1186for _, gidmap := range c.config.IDMappings.GIDMap {1187if gidmap.ContainerID == 0 {1188return gidmap.HostID1189}1190}1191return 01192}
1193
1194// IsInfra returns whether the container is an infra container
1195func (c *Container) IsInfra() bool {1196return c.config.IsInfra1197}
1198
1199// IsInitCtr returns whether the container is an init container
1200func (c *Container) IsInitCtr() bool {1201return len(c.config.InitContainerType) > 01202}
1203
1204// IsReadOnly returns whether the container is running in read-only mode
1205func (c *Container) IsReadOnly() bool {1206return c.config.Spec.Root.Readonly1207}
1208
1209// NetworkDisabled returns whether the container is running with a disabled network
1210func (c *Container) NetworkDisabled() (bool, error) {1211if c.config.NetNsCtr != "" {1212container, err := c.runtime.state.Container(c.config.NetNsCtr)1213if err != nil {1214return false, err1215}1216return container.NetworkDisabled()1217}1218return networkDisabled(c)1219}
1220
1221func (c *Container) HostNetwork() bool {1222if c.config.CreateNetNS || c.config.NetNsCtr != "" {1223return false1224}1225if c.config.Spec.Linux != nil {1226for _, ns := range c.config.Spec.Linux.Namespaces {1227if ns.Type == spec.NetworkNamespace {1228return false1229}1230}1231}1232return true1233}
1234
1235// HasHealthCheck returns bool as to whether there is a health check
1236// defined for the container
1237func (c *Container) HasHealthCheck() bool {1238return c.config.HealthCheckConfig != nil1239}
1240
1241// HealthCheckConfig returns the command and timing attributes of the health check
1242func (c *Container) HealthCheckConfig() *manifest.Schema2HealthConfig {1243return c.config.HealthCheckConfig1244}
1245
1246// AutoRemove indicates whether the container will be removed after it is executed
1247func (c *Container) AutoRemove() bool {1248spec := c.config.Spec1249if spec.Annotations == nil {1250return false1251}1252return spec.Annotations[define.InspectAnnotationAutoremove] == define.InspectResponseTrue1253}
1254
1255// Timezone returns the timezone configured inside the container.
1256// Local means it has the same timezone as the host machine
1257func (c *Container) Timezone() string {1258return c.config.Timezone1259}
1260
1261// Umask returns the Umask bits configured inside the container.
1262func (c *Container) Umask() string {1263return c.config.Umask1264}
1265
1266// Secrets return the secrets in the container
1267func (c *Container) Secrets() []*ContainerSecret {1268return c.config.Secrets1269}
1270
1271// Networks gets all the networks this container is connected to.
1272// Please do NOT use ctr.config.Networks, as this can be changed from those
1273// values at runtime via network connect and disconnect.
1274// Returned array of network names or error.
1275func (c *Container) Networks() ([]string, error) {1276if !c.batched {1277c.lock.Lock()1278defer c.lock.Unlock()1279
1280if err := c.syncContainer(); err != nil {1281return nil, err1282}1283}1284
1285networks, err := c.networks()1286if err != nil {1287return nil, err1288}1289
1290names := make([]string, 0, len(networks))1291
1292for name := range networks {1293names = append(names, name)1294}1295
1296return names, nil1297}
1298
1299// NetworkMode gets the configured network mode for the container.
1300// Get actual value from the database
1301func (c *Container) NetworkMode() string {1302networkMode := ""1303ctrSpec := c.config.Spec1304
1305switch {1306case c.config.CreateNetNS:1307// We actually store the network1308// mode for Slirp and Bridge, so1309// we can just use that1310networkMode = string(c.config.NetMode)1311case c.config.NetNsCtr != "":1312networkMode = fmt.Sprintf("container:%s", c.config.NetNsCtr)1313default:1314// Find the spec's network namespace.1315// If there is none, it's host networking.1316// If there is one and it has a path, it's "ns:".1317foundNetNS := false1318for _, ns := range ctrSpec.Linux.Namespaces {1319if ns.Type == spec.NetworkNamespace {1320foundNetNS = true1321if ns.Path != "" {1322networkMode = fmt.Sprintf("ns:%s", ns.Path)1323} else {1324// We're making a network ns, but not1325// configuring with Slirp or CNI. That1326// means it's --net=none1327networkMode = "none"1328}1329break1330}1331}1332if !foundNetNS {1333networkMode = "host"1334}1335}1336return networkMode1337}
1338
1339// Unlocked accessor for networks
1340func (c *Container) networks() (map[string]types.PerNetworkOptions, error) {1341return c.runtime.state.GetNetworks(c)1342}
1343
1344// getInterfaceByName returns a formatted interface name for a given
1345// network along with a bool as to whether the network existed
1346func (d ContainerNetworkDescriptions) getInterfaceByName(networkName string) (string, bool) {1347val, exists := d[networkName]1348if !exists {1349return "", exists1350}1351return fmt.Sprintf("eth%d", val), exists1352}
1353
1354// GetNetworkStatus returns the current network status for this container.
1355// This returns a map without deep copying which means this should only ever
1356// be used as read only access, do not modify this status.
1357func (c *Container) GetNetworkStatus() (map[string]types.StatusBlock, error) {1358if !c.batched {1359c.lock.Lock()1360defer c.lock.Unlock()1361
1362if err := c.syncContainer(); err != nil {1363return nil, err1364}1365}1366return c.getNetworkStatus(), nil1367}
1368
1369// getNetworkStatus get the current network status from the state. This function
1370// should be used instead of reading c.state.NetworkStatus directly.
1371func (c *Container) getNetworkStatus() map[string]types.StatusBlock {1372return c.state.NetworkStatus1373}
1374
1375func (c *Container) NamespaceMode(ns spec.LinuxNamespaceType, ctrSpec *spec.Spec) string {1376switch ns {1377case spec.UTSNamespace:1378if c.config.UTSNsCtr != "" {1379return fmt.Sprintf("container:%s", c.config.UTSNsCtr)1380}1381case spec.CgroupNamespace:1382if c.config.CgroupNsCtr != "" {1383return fmt.Sprintf("container:%s", c.config.CgroupNsCtr)1384}1385case spec.IPCNamespace:1386if c.config.IPCNsCtr != "" {1387return fmt.Sprintf("container:%s", c.config.IPCNsCtr)1388}1389case spec.PIDNamespace:1390if c.config.PIDNsCtr != "" {1391return fmt.Sprintf("container:%s", c.config.PIDNsCtr)1392}1393case spec.UserNamespace:1394if c.config.UserNsCtr != "" {1395return fmt.Sprintf("container:%s", c.config.UserNsCtr)1396}1397case spec.NetworkNamespace:1398if c.config.NetNsCtr != "" {1399return fmt.Sprintf("container:%s", c.config.NetNsCtr)1400}1401case spec.MountNamespace:1402if c.config.MountNsCtr != "" {1403return fmt.Sprintf("container:%s", c.config.MountNsCtr)1404}1405}1406
1407if ctrSpec.Linux != nil {1408// Locate the spec's given namespace.1409// If there is none, it's namespace=host.1410// If there is one and it has a path, it's "ns:".1411// If there is no path, it's default - the empty string.1412for _, availableNS := range ctrSpec.Linux.Namespaces {1413if availableNS.Type == ns {1414if availableNS.Path != "" {1415return fmt.Sprintf("ns:%s", availableNS.Path)1416}1417return "private"1418}1419}1420}1421return "host"1422}
1423