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