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