github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/container/container.go (about)

     1  package container // import "github.com/docker/docker/container"
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  	"sync"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/containerd/containerd/cio"
    18  	containertypes "github.com/docker/docker/api/types/container"
    19  	mounttypes "github.com/docker/docker/api/types/mount"
    20  	swarmtypes "github.com/docker/docker/api/types/swarm"
    21  	"github.com/docker/docker/container/stream"
    22  	"github.com/docker/docker/daemon/logger"
    23  	"github.com/docker/docker/daemon/logger/jsonfilelog"
    24  	"github.com/docker/docker/daemon/logger/local"
    25  	"github.com/docker/docker/daemon/logger/loggerutils/cache"
    26  	"github.com/docker/docker/daemon/network"
    27  	"github.com/docker/docker/errdefs"
    28  	"github.com/docker/docker/image"
    29  	"github.com/docker/docker/layer"
    30  	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
    31  	"github.com/docker/docker/pkg/containerfs"
    32  	"github.com/docker/docker/pkg/idtools"
    33  	"github.com/docker/docker/pkg/ioutils"
    34  	"github.com/docker/docker/pkg/system"
    35  	"github.com/docker/docker/restartmanager"
    36  	"github.com/docker/docker/volume"
    37  	volumemounts "github.com/docker/docker/volume/mounts"
    38  	units "github.com/docker/go-units"
    39  	agentexec "github.com/moby/swarmkit/v2/agent/exec"
    40  	"github.com/moby/sys/signal"
    41  	"github.com/moby/sys/symlink"
    42  	"github.com/pkg/errors"
    43  	"github.com/sirupsen/logrus"
    44  )
    45  
    46  const (
    47  	configFileName     = "config.v2.json"
    48  	hostConfigFileName = "hostconfig.json"
    49  )
    50  
    51  // ExitStatus provides exit reasons for a container.
    52  type ExitStatus struct {
    53  	// The exit code with which the container exited.
    54  	ExitCode int
    55  
    56  	// Time at which the container died
    57  	ExitedAt time.Time
    58  }
    59  
    60  // Container holds the structure defining a container object.
    61  type Container struct {
    62  	StreamConfig *stream.Config
    63  	// embed for Container to support states directly.
    64  	*State          `json:"State"` // Needed for Engine API version <= 1.11
    65  	Root            string         `json:"-"` // Path to the "home" of the container, including metadata.
    66  	BaseFS          string         `json:"-"` // Path to the graphdriver mountpoint
    67  	RWLayer         layer.RWLayer  `json:"-"`
    68  	ID              string
    69  	Created         time.Time
    70  	Managed         bool
    71  	Path            string
    72  	Args            []string
    73  	Config          *containertypes.Config
    74  	ImageID         image.ID `json:"Image"`
    75  	NetworkSettings *network.Settings
    76  	LogPath         string
    77  	Name            string
    78  	Driver          string
    79  	OS              string
    80  	// MountLabel contains the options for the 'mount' command
    81  	MountLabel               string
    82  	ProcessLabel             string
    83  	RestartCount             int
    84  	HasBeenStartedBefore     bool
    85  	HasBeenManuallyStopped   bool // used for unless-stopped restart policy
    86  	HasBeenManuallyRestarted bool `json:"-"` // used to distinguish restart caused by restart policy from the manual one
    87  	MountPoints              map[string]*volumemounts.MountPoint
    88  	HostConfig               *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
    89  	ExecCommands             *ExecStore                 `json:"-"`
    90  	DependencyStore          agentexec.DependencyGetter `json:"-"`
    91  	SecretReferences         []*swarmtypes.SecretReference
    92  	ConfigReferences         []*swarmtypes.ConfigReference
    93  	// logDriver for closing
    94  	LogDriver      logger.Logger  `json:"-"`
    95  	LogCopier      *logger.Copier `json:"-"`
    96  	restartManager restartmanager.RestartManager
    97  	attachContext  *attachContext
    98  
    99  	// Fields here are specific to Unix platforms
   100  	AppArmorProfile string
   101  	HostnamePath    string
   102  	HostsPath       string
   103  	ShmPath         string
   104  	ResolvConfPath  string
   105  	SeccompProfile  string
   106  	NoNewPrivileges bool
   107  
   108  	// Fields here are specific to Windows
   109  	NetworkSharedContainerID string            `json:"-"`
   110  	SharedEndpointList       []string          `json:"-"`
   111  	LocalLogCacheMeta        localLogCacheMeta `json:",omitempty"`
   112  }
   113  
   114  type localLogCacheMeta struct {
   115  	HaveNotifyEnabled bool
   116  }
   117  
   118  // NewBaseContainer creates a new container with its
   119  // basic configuration.
   120  func NewBaseContainer(id, root string) *Container {
   121  	return &Container{
   122  		ID:            id,
   123  		State:         NewState(),
   124  		ExecCommands:  NewExecStore(),
   125  		Root:          root,
   126  		MountPoints:   make(map[string]*volumemounts.MountPoint),
   127  		StreamConfig:  stream.NewConfig(),
   128  		attachContext: &attachContext{},
   129  	}
   130  }
   131  
   132  // FromDisk loads the container configuration stored in the host.
   133  func (container *Container) FromDisk() error {
   134  	pth, err := container.ConfigPath()
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	jsonSource, err := os.Open(pth)
   140  	if err != nil {
   141  		return err
   142  	}
   143  	defer jsonSource.Close()
   144  
   145  	dec := json.NewDecoder(jsonSource)
   146  
   147  	// Load container settings
   148  	if err := dec.Decode(container); err != nil {
   149  		return err
   150  	}
   151  
   152  	// Ensure the operating system is set if blank. Assume it is the OS of the
   153  	// host OS if not, to ensure containers created before multiple-OS
   154  	// support are migrated
   155  	if container.OS == "" {
   156  		container.OS = runtime.GOOS
   157  	}
   158  
   159  	return container.readHostConfig()
   160  }
   161  
   162  // toDisk writes the container's configuration (config.v2.json, hostconfig.json)
   163  // to disk and returns a deep copy.
   164  func (container *Container) toDisk() (*Container, error) {
   165  	pth, err := container.ConfigPath()
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	// Save container settings
   171  	f, err := ioutils.NewAtomicFileWriter(pth, 0600)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	defer f.Close()
   176  
   177  	var buf bytes.Buffer
   178  	w := io.MultiWriter(&buf, f)
   179  	if err := json.NewEncoder(w).Encode(container); err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	var deepCopy Container
   184  	if err := json.NewDecoder(&buf).Decode(&deepCopy); err != nil {
   185  		return nil, err
   186  	}
   187  	deepCopy.HostConfig, err = container.WriteHostConfig()
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	return &deepCopy, nil
   192  }
   193  
   194  // CheckpointTo makes the Container's current state visible to queries, and persists state.
   195  // Callers must hold a Container lock.
   196  func (container *Container) CheckpointTo(store *ViewDB) error {
   197  	deepCopy, err := container.toDisk()
   198  	if err != nil {
   199  		return err
   200  	}
   201  	return store.Save(deepCopy)
   202  }
   203  
   204  // readHostConfig reads the host configuration from disk for the container.
   205  func (container *Container) readHostConfig() error {
   206  	container.HostConfig = &containertypes.HostConfig{}
   207  	// If the hostconfig file does not exist, do not read it.
   208  	// (We still have to initialize container.HostConfig,
   209  	// but that's OK, since we just did that above.)
   210  	pth, err := container.HostConfigPath()
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	f, err := os.Open(pth)
   216  	if err != nil {
   217  		if os.IsNotExist(err) {
   218  			return nil
   219  		}
   220  		return err
   221  	}
   222  	defer f.Close()
   223  
   224  	if err := json.NewDecoder(f).Decode(&container.HostConfig); err != nil {
   225  		return err
   226  	}
   227  
   228  	container.InitDNSHostConfig()
   229  
   230  	return nil
   231  }
   232  
   233  // WriteHostConfig saves the host configuration on disk for the container,
   234  // and returns a deep copy of the saved object. Callers must hold a Container lock.
   235  func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error) {
   236  	var (
   237  		buf      bytes.Buffer
   238  		deepCopy containertypes.HostConfig
   239  	)
   240  
   241  	pth, err := container.HostConfigPath()
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	f, err := ioutils.NewAtomicFileWriter(pth, 0600)
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  	defer f.Close()
   251  
   252  	w := io.MultiWriter(&buf, f)
   253  	if err := json.NewEncoder(w).Encode(&container.HostConfig); err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	if err := json.NewDecoder(&buf).Decode(&deepCopy); err != nil {
   258  		return nil, err
   259  	}
   260  	return &deepCopy, nil
   261  }
   262  
   263  // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
   264  func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity) error {
   265  	if container.Config.WorkingDir == "" {
   266  		return nil
   267  	}
   268  
   269  	container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir)
   270  	pth, err := container.GetResourcePath(container.Config.WorkingDir)
   271  	if err != nil {
   272  		return err
   273  	}
   274  
   275  	if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIdentity); err != nil {
   276  		pthInfo, err2 := os.Stat(pth)
   277  		if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
   278  			return errors.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
   279  		}
   280  
   281  		return err
   282  	}
   283  
   284  	return nil
   285  }
   286  
   287  // GetResourcePath evaluates `path` in the scope of the container's BaseFS, with proper path
   288  // sanitisation. Symlinks are all scoped to the BaseFS of the container, as
   289  // though the container's BaseFS was `/`.
   290  //
   291  // The BaseFS of a container is the host-facing path which is bind-mounted as
   292  // `/` inside the container. This method is essentially used to access a
   293  // particular path inside the container as though you were a process in that
   294  // container.
   295  //
   296  // # NOTE
   297  // The returned path is *only* safely scoped inside the container's BaseFS
   298  // if no component of the returned path changes (such as a component
   299  // symlinking to a different path) between using this method and using the
   300  // path. See symlink.FollowSymlinkInScope for more details.
   301  func (container *Container) GetResourcePath(path string) (string, error) {
   302  	if container.BaseFS == "" {
   303  		return "", errors.New("GetResourcePath: BaseFS of container " + container.ID + " is unexpectedly empty")
   304  	}
   305  	// IMPORTANT - These are paths on the OS where the daemon is running, hence
   306  	// any filepath operations must be done in an OS agnostic way.
   307  	r, e := containerfs.ResolveScopedPath(container.BaseFS, containerfs.CleanScopedPath(path))
   308  
   309  	// Log this here on the daemon side as there's otherwise no indication apart
   310  	// from the error being propagated all the way back to the client. This makes
   311  	// debugging significantly easier and clearly indicates the error comes from the daemon.
   312  	if e != nil {
   313  		logrus.Errorf("Failed to ResolveScopedPath BaseFS %s path %s %s\n", container.BaseFS, path, e)
   314  	}
   315  	return r, e
   316  }
   317  
   318  // GetRootResourcePath evaluates `path` in the scope of the container's root, with proper path
   319  // sanitisation. Symlinks are all scoped to the root of the container, as
   320  // though the container's root was `/`.
   321  //
   322  // The root of a container is the host-facing configuration metadata directory.
   323  // Only use this method to safely access the container's `container.json` or
   324  // other metadata files. If in doubt, use container.GetResourcePath.
   325  //
   326  // # NOTE
   327  // The returned path is *only* safely scoped inside the container's root
   328  // if no component of the returned path changes (such as a component
   329  // symlinking to a different path) between using this method and using the
   330  // path. See symlink.FollowSymlinkInScope for more details.
   331  func (container *Container) GetRootResourcePath(path string) (string, error) {
   332  	// IMPORTANT - These are paths on the OS where the daemon is running, hence
   333  	// any filepath operations must be done in an OS agnostic way.
   334  	cleanPath := filepath.Join(string(os.PathSeparator), path)
   335  	return symlink.FollowSymlinkInScope(filepath.Join(container.Root, cleanPath), container.Root)
   336  }
   337  
   338  // ExitOnNext signals to the monitor that it should not restart the container
   339  // after we send the kill signal.
   340  func (container *Container) ExitOnNext() {
   341  	container.RestartManager().Cancel()
   342  }
   343  
   344  // HostConfigPath returns the path to the container's JSON hostconfig
   345  func (container *Container) HostConfigPath() (string, error) {
   346  	return container.GetRootResourcePath(hostConfigFileName)
   347  }
   348  
   349  // ConfigPath returns the path to the container's JSON config
   350  func (container *Container) ConfigPath() (string, error) {
   351  	return container.GetRootResourcePath(configFileName)
   352  }
   353  
   354  // CheckpointDir returns the directory checkpoints are stored in
   355  func (container *Container) CheckpointDir() string {
   356  	return filepath.Join(container.Root, "checkpoints")
   357  }
   358  
   359  // StartLogger starts a new logger driver for the container.
   360  func (container *Container) StartLogger() (logger.Logger, error) {
   361  	cfg := container.HostConfig.LogConfig
   362  	initDriver, err := logger.GetLogDriver(cfg.Type)
   363  	if err != nil {
   364  		return nil, errors.Wrap(err, "failed to get logging factory")
   365  	}
   366  	info := logger.Info{
   367  		Config:              cfg.Config,
   368  		ContainerID:         container.ID,
   369  		ContainerName:       container.Name,
   370  		ContainerEntrypoint: container.Path,
   371  		ContainerArgs:       container.Args,
   372  		ContainerImageID:    container.ImageID.String(),
   373  		ContainerImageName:  container.Config.Image,
   374  		ContainerCreated:    container.Created,
   375  		ContainerEnv:        container.Config.Env,
   376  		ContainerLabels:     container.Config.Labels,
   377  		DaemonName:          "docker",
   378  	}
   379  
   380  	// Set logging file for "json-logger"
   381  	// TODO(@cpuguy83): Setup here based on log driver is a little weird.
   382  	switch cfg.Type {
   383  	case jsonfilelog.Name:
   384  		info.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID))
   385  		if err != nil {
   386  			return nil, err
   387  		}
   388  
   389  		container.LogPath = info.LogPath
   390  	case local.Name:
   391  		// Do not set container.LogPath for the local driver
   392  		// This would expose the value to the API, which should not be done as it means
   393  		// that the log file implementation would become a stable API that cannot change.
   394  		logDir, err := container.GetRootResourcePath("local-logs")
   395  		if err != nil {
   396  			return nil, err
   397  		}
   398  		if err := os.MkdirAll(logDir, 0700); err != nil {
   399  			return nil, errdefs.System(errors.Wrap(err, "error creating local logs dir"))
   400  		}
   401  		info.LogPath = filepath.Join(logDir, "container.log")
   402  	}
   403  
   404  	l, err := initDriver(info)
   405  	if err != nil {
   406  		return nil, err
   407  	}
   408  
   409  	if containertypes.LogMode(cfg.Config["mode"]) == containertypes.LogModeNonBlock {
   410  		bufferSize := int64(-1)
   411  		if s, exists := cfg.Config["max-buffer-size"]; exists {
   412  			bufferSize, err = units.RAMInBytes(s)
   413  			if err != nil {
   414  				return nil, err
   415  			}
   416  		}
   417  		l = logger.NewRingLogger(l, info, bufferSize)
   418  	}
   419  
   420  	if _, ok := l.(logger.LogReader); !ok {
   421  		if cache.ShouldUseCache(cfg.Config) {
   422  			logPath, err := container.GetRootResourcePath("container-cached.log")
   423  			if err != nil {
   424  				return nil, err
   425  			}
   426  
   427  			if !container.LocalLogCacheMeta.HaveNotifyEnabled {
   428  				logrus.WithField("container", container.ID).WithField("driver", container.HostConfig.LogConfig.Type).Info("Configured log driver does not support reads, enabling local file cache for container logs")
   429  				container.LocalLogCacheMeta.HaveNotifyEnabled = true
   430  			}
   431  			info.LogPath = logPath
   432  			l, err = cache.WithLocalCache(l, info)
   433  			if err != nil {
   434  				return nil, errors.Wrap(err, "error setting up local container log cache")
   435  			}
   436  		}
   437  	}
   438  	return l, nil
   439  }
   440  
   441  // GetProcessLabel returns the process label for the container.
   442  func (container *Container) GetProcessLabel() string {
   443  	// even if we have a process label return "" if we are running
   444  	// in privileged mode
   445  	if container.HostConfig.Privileged {
   446  		return ""
   447  	}
   448  	return container.ProcessLabel
   449  }
   450  
   451  // GetMountLabel returns the mounting label for the container.
   452  // This label is empty if the container is privileged.
   453  func (container *Container) GetMountLabel() string {
   454  	return container.MountLabel
   455  }
   456  
   457  // GetExecIDs returns the list of exec commands running on the container.
   458  func (container *Container) GetExecIDs() []string {
   459  	return container.ExecCommands.List()
   460  }
   461  
   462  // ShouldRestart decides whether the daemon should restart the container or not.
   463  // This is based on the container's restart policy.
   464  func (container *Container) ShouldRestart() bool {
   465  	shouldRestart, _, _ := container.RestartManager().ShouldRestart(uint32(container.ExitCode()), container.HasBeenManuallyStopped, container.FinishedAt.Sub(container.StartedAt))
   466  	return shouldRestart
   467  }
   468  
   469  // AddMountPointWithVolume adds a new mount point configured with a volume to the container.
   470  func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
   471  	volumeParser := volumemounts.NewParser()
   472  	container.MountPoints[destination] = &volumemounts.MountPoint{
   473  		Type:        mounttypes.TypeVolume,
   474  		Name:        vol.Name(),
   475  		Driver:      vol.DriverName(),
   476  		Destination: destination,
   477  		RW:          rw,
   478  		Volume:      vol,
   479  		CopyData:    volumeParser.DefaultCopyMode(),
   480  	}
   481  }
   482  
   483  // UnmountVolumes unmounts all volumes
   484  func (container *Container) UnmountVolumes(volumeEventLog func(name, action string, attributes map[string]string)) error {
   485  	var errors []string
   486  	for _, volumeMount := range container.MountPoints {
   487  		if volumeMount.Volume == nil {
   488  			continue
   489  		}
   490  
   491  		if err := volumeMount.Cleanup(); err != nil {
   492  			errors = append(errors, err.Error())
   493  			continue
   494  		}
   495  
   496  		attributes := map[string]string{
   497  			"driver":    volumeMount.Volume.DriverName(),
   498  			"container": container.ID,
   499  		}
   500  		volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes)
   501  	}
   502  	if len(errors) > 0 {
   503  		return fmt.Errorf("error while unmounting volumes for container %s: %s", container.ID, strings.Join(errors, "; "))
   504  	}
   505  	return nil
   506  }
   507  
   508  // IsDestinationMounted checks whether a path is mounted on the container or not.
   509  func (container *Container) IsDestinationMounted(destination string) bool {
   510  	return container.MountPoints[destination] != nil
   511  }
   512  
   513  // StopSignal returns the signal used to stop the container.
   514  func (container *Container) StopSignal() syscall.Signal {
   515  	var stopSignal syscall.Signal
   516  	if container.Config.StopSignal != "" {
   517  		stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
   518  	}
   519  
   520  	if stopSignal == 0 {
   521  		stopSignal, _ = signal.ParseSignal(defaultStopSignal)
   522  	}
   523  	return stopSignal
   524  }
   525  
   526  // StopTimeout returns the timeout (in seconds) used to stop the container.
   527  func (container *Container) StopTimeout() int {
   528  	if container.Config.StopTimeout != nil {
   529  		return *container.Config.StopTimeout
   530  	}
   531  	return defaultStopTimeout
   532  }
   533  
   534  // InitDNSHostConfig ensures that the dns fields are never nil.
   535  // New containers don't ever have those fields nil,
   536  // but pre created containers can still have those nil values.
   537  // The non-recommended host configuration in the start api can
   538  // make these fields nil again, this corrects that issue until
   539  // we remove that behavior for good.
   540  // See https://github.com/docker/docker/pull/17779
   541  // for a more detailed explanation on why we don't want that.
   542  func (container *Container) InitDNSHostConfig() {
   543  	container.Lock()
   544  	defer container.Unlock()
   545  	if container.HostConfig.DNS == nil {
   546  		container.HostConfig.DNS = make([]string, 0)
   547  	}
   548  
   549  	if container.HostConfig.DNSSearch == nil {
   550  		container.HostConfig.DNSSearch = make([]string, 0)
   551  	}
   552  
   553  	if container.HostConfig.DNSOptions == nil {
   554  		container.HostConfig.DNSOptions = make([]string, 0)
   555  	}
   556  }
   557  
   558  // UpdateMonitor updates monitor configure for running container
   559  func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPolicy) {
   560  	type policySetter interface {
   561  		SetPolicy(containertypes.RestartPolicy)
   562  	}
   563  
   564  	if rm, ok := container.RestartManager().(policySetter); ok {
   565  		rm.SetPolicy(restartPolicy)
   566  	}
   567  }
   568  
   569  // FullHostname returns hostname and optional domain appended to it.
   570  func (container *Container) FullHostname() string {
   571  	fullHostname := container.Config.Hostname
   572  	if container.Config.Domainname != "" {
   573  		fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname)
   574  	}
   575  	return fullHostname
   576  }
   577  
   578  // RestartManager returns the current restartmanager instance connected to container.
   579  func (container *Container) RestartManager() restartmanager.RestartManager {
   580  	if container.restartManager == nil {
   581  		container.restartManager = restartmanager.New(container.HostConfig.RestartPolicy, container.RestartCount)
   582  	}
   583  	return container.restartManager
   584  }
   585  
   586  // ResetRestartManager initializes new restartmanager based on container config
   587  func (container *Container) ResetRestartManager(resetCount bool) {
   588  	if container.restartManager != nil {
   589  		container.restartManager.Cancel()
   590  	}
   591  	if resetCount {
   592  		container.RestartCount = 0
   593  	}
   594  	container.restartManager = nil
   595  }
   596  
   597  type attachContext struct {
   598  	ctx    context.Context
   599  	cancel context.CancelFunc
   600  	mu     sync.Mutex
   601  }
   602  
   603  // InitAttachContext initializes or returns existing context for attach calls to
   604  // track container liveness.
   605  func (container *Container) InitAttachContext() context.Context {
   606  	container.attachContext.mu.Lock()
   607  	defer container.attachContext.mu.Unlock()
   608  	if container.attachContext.ctx == nil {
   609  		container.attachContext.ctx, container.attachContext.cancel = context.WithCancel(context.Background())
   610  	}
   611  	return container.attachContext.ctx
   612  }
   613  
   614  // CancelAttachContext cancels attach context. All attach calls should detach
   615  // after this call.
   616  func (container *Container) CancelAttachContext() {
   617  	container.attachContext.mu.Lock()
   618  	if container.attachContext.ctx != nil {
   619  		container.attachContext.cancel()
   620  		container.attachContext.ctx = nil
   621  	}
   622  	container.attachContext.mu.Unlock()
   623  }
   624  
   625  func (container *Container) startLogging() error {
   626  	if container.HostConfig.LogConfig.Type == "none" {
   627  		return nil // do not start logging routines
   628  	}
   629  
   630  	l, err := container.StartLogger()
   631  	if err != nil {
   632  		return fmt.Errorf("failed to initialize logging driver: %v", err)
   633  	}
   634  
   635  	copier := logger.NewCopier(map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
   636  	container.LogCopier = copier
   637  	copier.Run()
   638  	container.LogDriver = l
   639  
   640  	return nil
   641  }
   642  
   643  // StdinPipe gets the stdin stream of the container
   644  func (container *Container) StdinPipe() io.WriteCloser {
   645  	return container.StreamConfig.StdinPipe()
   646  }
   647  
   648  // StdoutPipe gets the stdout stream of the container
   649  func (container *Container) StdoutPipe() io.ReadCloser {
   650  	return container.StreamConfig.StdoutPipe()
   651  }
   652  
   653  // StderrPipe gets the stderr stream of the container
   654  func (container *Container) StderrPipe() io.ReadCloser {
   655  	return container.StreamConfig.StderrPipe()
   656  }
   657  
   658  // CloseStreams closes the container's stdio streams
   659  func (container *Container) CloseStreams() error {
   660  	return container.StreamConfig.CloseStreams()
   661  }
   662  
   663  // InitializeStdio is called by libcontainerd to connect the stdio.
   664  func (container *Container) InitializeStdio(iop *cio.DirectIO) (cio.IO, error) {
   665  	if err := container.startLogging(); err != nil {
   666  		container.Reset(false)
   667  		return nil, err
   668  	}
   669  
   670  	container.StreamConfig.CopyToPipe(iop)
   671  
   672  	if container.StreamConfig.Stdin() == nil && !container.Config.Tty {
   673  		if iop.Stdin != nil {
   674  			if err := iop.Stdin.Close(); err != nil {
   675  				logrus.Warnf("error closing stdin: %+v", err)
   676  			}
   677  		}
   678  	}
   679  
   680  	return &rio{IO: iop, sc: container.StreamConfig}, nil
   681  }
   682  
   683  // MountsResourcePath returns the path where mounts are stored for the given mount
   684  func (container *Container) MountsResourcePath(mount string) (string, error) {
   685  	return container.GetRootResourcePath(filepath.Join("mounts", mount))
   686  }
   687  
   688  // SecretMountPath returns the path of the secret mount for the container
   689  func (container *Container) SecretMountPath() (string, error) {
   690  	return container.MountsResourcePath("secrets")
   691  }
   692  
   693  // SecretFilePath returns the path to the location of a secret on the host.
   694  func (container *Container) SecretFilePath(secretRef swarmtypes.SecretReference) (string, error) {
   695  	secrets, err := container.SecretMountPath()
   696  	if err != nil {
   697  		return "", err
   698  	}
   699  	return filepath.Join(secrets, secretRef.SecretID), nil
   700  }
   701  
   702  func getSecretTargetPath(r *swarmtypes.SecretReference) string {
   703  	if filepath.IsAbs(r.File.Name) {
   704  		return r.File.Name
   705  	}
   706  
   707  	return filepath.Join(containerSecretMountPath, r.File.Name)
   708  }
   709  
   710  // getConfigTargetPath makes sure that config paths inside the container are
   711  // absolute, as required by the runtime spec, and enforced by runc >= 1.0.0-rc94.
   712  // see https://github.com/opencontainers/runc/issues/2928
   713  func getConfigTargetPath(r *swarmtypes.ConfigReference) string {
   714  	if filepath.IsAbs(r.File.Name) {
   715  		return r.File.Name
   716  	}
   717  
   718  	return filepath.Join(containerConfigMountPath, r.File.Name)
   719  }
   720  
   721  // CreateDaemonEnvironment creates a new environment variable slice for this container.
   722  func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string {
   723  	// Setup environment
   724  	ctrOS := container.OS
   725  	if ctrOS == "" {
   726  		ctrOS = runtime.GOOS
   727  	}
   728  
   729  	// Figure out what size slice we need so we can allocate this all at once.
   730  	envSize := len(container.Config.Env)
   731  	if runtime.GOOS != "windows" {
   732  		envSize += 2 + len(linkedEnv)
   733  	}
   734  	if tty {
   735  		envSize++
   736  	}
   737  
   738  	env := make([]string, 0, envSize)
   739  	if runtime.GOOS != "windows" {
   740  		env = append(env, "PATH="+system.DefaultPathEnv(ctrOS))
   741  		env = append(env, "HOSTNAME="+container.Config.Hostname)
   742  		if tty {
   743  			env = append(env, "TERM=xterm")
   744  		}
   745  		env = append(env, linkedEnv...)
   746  	}
   747  
   748  	// because the env on the container can override certain default values
   749  	// we need to replace the 'env' keys where they match and append anything
   750  	// else.
   751  	env = ReplaceOrAppendEnvValues(env, container.Config.Env)
   752  	return env
   753  }
   754  
   755  // RestoreTask restores the containerd container and task handles and reattaches
   756  // the IO for the running task. Container state is not synced with containerd's
   757  // state.
   758  //
   759  // An errdefs.NotFound error is returned if the container does not exist in
   760  // containerd. However, a nil error is returned if the task does not exist in
   761  // containerd.
   762  func (container *Container) RestoreTask(ctx context.Context, client libcontainerdtypes.Client) error {
   763  	container.Lock()
   764  	defer container.Unlock()
   765  	var err error
   766  	container.ctr, err = client.LoadContainer(ctx, container.ID)
   767  	if err != nil {
   768  		return err
   769  	}
   770  	container.task, err = container.ctr.AttachTask(ctx, container.InitializeStdio)
   771  	if err != nil && !errdefs.IsNotFound(err) {
   772  		return err
   773  	}
   774  	return nil
   775  }
   776  
   777  // GetRunningTask asserts that the container is running and returns the Task for
   778  // the container. An errdefs.Conflict error is returned if the container is not
   779  // in the Running state.
   780  //
   781  // A system error is returned if container is in a bad state: Running is true
   782  // but has a nil Task.
   783  //
   784  // The container lock must be held when calling this method.
   785  func (container *Container) GetRunningTask() (libcontainerdtypes.Task, error) {
   786  	if !container.Running {
   787  		return nil, errdefs.Conflict(fmt.Errorf("container %s is not running", container.ID))
   788  	}
   789  	tsk, ok := container.Task()
   790  	if !ok {
   791  		return nil, errdefs.System(errors.WithStack(fmt.Errorf("container %s is in Running state but has no containerd Task set", container.ID)))
   792  	}
   793  	return tsk, nil
   794  }
   795  
   796  type rio struct {
   797  	cio.IO
   798  
   799  	sc *stream.Config
   800  }
   801  
   802  func (i *rio) Close() error {
   803  	i.IO.Close()
   804  
   805  	return i.sc.CloseStreams()
   806  }
   807  
   808  func (i *rio) Wait() {
   809  	i.sc.Wait(context.Background())
   810  
   811  	i.IO.Wait()
   812  }