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