github.com/baris/docker@v1.7.0/daemon/container.go (about) 1 package daemon 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 "sync" 13 "syscall" 14 "time" 15 16 "github.com/docker/libcontainer/label" 17 18 "github.com/Sirupsen/logrus" 19 "github.com/docker/docker/daemon/execdriver" 20 "github.com/docker/docker/daemon/logger" 21 "github.com/docker/docker/daemon/logger/jsonfilelog" 22 "github.com/docker/docker/daemon/network" 23 "github.com/docker/docker/image" 24 "github.com/docker/docker/nat" 25 "github.com/docker/docker/pkg/archive" 26 "github.com/docker/docker/pkg/broadcastwriter" 27 "github.com/docker/docker/pkg/ioutils" 28 "github.com/docker/docker/pkg/jsonlog" 29 "github.com/docker/docker/pkg/mount" 30 "github.com/docker/docker/pkg/promise" 31 "github.com/docker/docker/pkg/symlink" 32 "github.com/docker/docker/runconfig" 33 "github.com/docker/docker/volume" 34 ) 35 36 var ( 37 ErrNotATTY = errors.New("The PTY is not a file") 38 ErrNoTTY = errors.New("No PTY found") 39 ErrContainerStart = errors.New("The container failed to start. Unknown error") 40 ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.") 41 ) 42 43 type StreamConfig struct { 44 stdout *broadcastwriter.BroadcastWriter 45 stderr *broadcastwriter.BroadcastWriter 46 stdin io.ReadCloser 47 stdinPipe io.WriteCloser 48 } 49 50 // CommonContainer holds the settings for a container which are applicable 51 // across all platforms supported by the daemon. 52 type CommonContainer struct { 53 StreamConfig 54 55 *State `json:"State"` // Needed for remote api version <= 1.11 56 root string // Path to the "home" of the container, including metadata. 57 basefs string // Path to the graphdriver mountpoint 58 59 ID string 60 Created time.Time 61 Path string 62 Args []string 63 Config *runconfig.Config 64 ImageID string `json:"Image"` 65 NetworkSettings *network.Settings 66 ResolvConfPath string 67 HostnamePath string 68 HostsPath string 69 LogPath string 70 Name string 71 Driver string 72 ExecDriver string 73 MountLabel, ProcessLabel string 74 RestartCount int 75 UpdateDns bool 76 77 MountPoints map[string]*mountPoint 78 Volumes map[string]string // Deprecated since 1.7, kept for backwards compatibility 79 VolumesRW map[string]bool // Deprecated since 1.7, kept for backwards compatibility 80 81 hostConfig *runconfig.HostConfig 82 command *execdriver.Command 83 84 monitor *containerMonitor 85 execCommands *execStore 86 daemon *Daemon 87 // logDriver for closing 88 logDriver logger.Logger 89 logCopier *logger.Copier 90 } 91 92 func (container *Container) FromDisk() error { 93 pth, err := container.jsonPath() 94 if err != nil { 95 return err 96 } 97 98 jsonSource, err := os.Open(pth) 99 if err != nil { 100 return err 101 } 102 defer jsonSource.Close() 103 104 dec := json.NewDecoder(jsonSource) 105 106 // Load container settings 107 // udp broke compat of docker.PortMapping, but it's not used when loading a container, we can skip it 108 if err := dec.Decode(container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") { 109 return err 110 } 111 112 if err := label.ReserveLabel(container.ProcessLabel); err != nil { 113 return err 114 } 115 return container.readHostConfig() 116 } 117 118 func (container *Container) toDisk() error { 119 data, err := json.Marshal(container) 120 if err != nil { 121 return err 122 } 123 124 pth, err := container.jsonPath() 125 if err != nil { 126 return err 127 } 128 129 if err := ioutil.WriteFile(pth, data, 0666); err != nil { 130 return err 131 } 132 133 return container.WriteHostConfig() 134 } 135 136 func (container *Container) ToDisk() error { 137 container.Lock() 138 err := container.toDisk() 139 container.Unlock() 140 return err 141 } 142 143 func (container *Container) readHostConfig() error { 144 container.hostConfig = &runconfig.HostConfig{} 145 // If the hostconfig file does not exist, do not read it. 146 // (We still have to initialize container.hostConfig, 147 // but that's OK, since we just did that above.) 148 pth, err := container.hostConfigPath() 149 if err != nil { 150 return err 151 } 152 153 _, err = os.Stat(pth) 154 if os.IsNotExist(err) { 155 return nil 156 } 157 158 f, err := os.Open(pth) 159 if err != nil { 160 return err 161 } 162 defer f.Close() 163 164 return json.NewDecoder(f).Decode(&container.hostConfig) 165 } 166 167 func (container *Container) WriteHostConfig() error { 168 data, err := json.Marshal(container.hostConfig) 169 if err != nil { 170 return err 171 } 172 173 pth, err := container.hostConfigPath() 174 if err != nil { 175 return err 176 } 177 178 return ioutil.WriteFile(pth, data, 0666) 179 } 180 181 func (container *Container) LogEvent(action string) { 182 d := container.daemon 183 d.EventsService.Log( 184 action, 185 container.ID, 186 container.Config.Image, 187 ) 188 } 189 190 // Evaluates `path` in the scope of the container's basefs, with proper path 191 // sanitisation. Symlinks are all scoped to the basefs of the container, as 192 // though the container's basefs was `/`. 193 // 194 // The basefs of a container is the host-facing path which is bind-mounted as 195 // `/` inside the container. This method is essentially used to access a 196 // particular path inside the container as though you were a process in that 197 // container. 198 // 199 // NOTE: The returned path is *only* safely scoped inside the container's basefs 200 // if no component of the returned path changes (such as a component 201 // symlinking to a different path) between using this method and using the 202 // path. See symlink.FollowSymlinkInScope for more details. 203 func (container *Container) GetResourcePath(path string) (string, error) { 204 cleanPath := filepath.Join("/", path) 205 return symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs) 206 } 207 208 // Evaluates `path` in the scope of the container's root, with proper path 209 // sanitisation. Symlinks are all scoped to the root of the container, as 210 // though the container's root was `/`. 211 // 212 // The root of a container is the host-facing configuration metadata directory. 213 // Only use this method to safely access the container's `container.json` or 214 // other metadata files. If in doubt, use container.GetResourcePath. 215 // 216 // NOTE: The returned path is *only* safely scoped inside the container's root 217 // if no component of the returned path changes (such as a component 218 // symlinking to a different path) between using this method and using the 219 // path. See symlink.FollowSymlinkInScope for more details. 220 func (container *Container) GetRootResourcePath(path string) (string, error) { 221 cleanPath := filepath.Join("/", path) 222 return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root) 223 } 224 225 func (container *Container) Start() (err error) { 226 container.Lock() 227 defer container.Unlock() 228 229 if container.Running { 230 return nil 231 } 232 233 if container.removalInProgress || container.Dead { 234 return fmt.Errorf("Container is marked for removal and cannot be started.") 235 } 236 237 // if we encounter an error during start we need to ensure that any other 238 // setup has been cleaned up properly 239 defer func() { 240 if err != nil { 241 container.setError(err) 242 // if no one else has set it, make sure we don't leave it at zero 243 if container.ExitCode == 0 { 244 container.ExitCode = 128 245 } 246 container.toDisk() 247 container.cleanup() 248 } 249 }() 250 251 if err := container.Mount(); err != nil { 252 return err 253 } 254 if err := container.initializeNetworking(); err != nil { 255 return err 256 } 257 container.verifyDaemonSettings() 258 linkedEnv, err := container.setupLinkedContainers() 259 if err != nil { 260 return err 261 } 262 if err := container.setupWorkingDirectory(); err != nil { 263 return err 264 } 265 env := container.createDaemonEnvironment(linkedEnv) 266 if err := populateCommand(container, env); err != nil { 267 return err 268 } 269 270 mounts, err := container.setupMounts() 271 if err != nil { 272 return err 273 } 274 275 container.command.Mounts = mounts 276 return container.waitForStart() 277 } 278 279 func (container *Container) Run() error { 280 if err := container.Start(); err != nil { 281 return err 282 } 283 container.WaitStop(-1 * time.Second) 284 return nil 285 } 286 287 func (container *Container) Output() (output []byte, err error) { 288 pipe := container.StdoutPipe() 289 defer pipe.Close() 290 if err := container.Start(); err != nil { 291 return nil, err 292 } 293 output, err = ioutil.ReadAll(pipe) 294 container.WaitStop(-1 * time.Second) 295 return output, err 296 } 297 298 // StreamConfig.StdinPipe returns a WriteCloser which can be used to feed data 299 // to the standard input of the container's active process. 300 // Container.StdoutPipe and Container.StderrPipe each return a ReadCloser 301 // which can be used to retrieve the standard output (and error) generated 302 // by the container's active process. The output (and error) are actually 303 // copied and delivered to all StdoutPipe and StderrPipe consumers, using 304 // a kind of "broadcaster". 305 306 func (streamConfig *StreamConfig) StdinPipe() io.WriteCloser { 307 return streamConfig.stdinPipe 308 } 309 310 func (streamConfig *StreamConfig) StdoutPipe() io.ReadCloser { 311 reader, writer := io.Pipe() 312 streamConfig.stdout.AddWriter(writer, "") 313 return ioutils.NewBufReader(reader) 314 } 315 316 func (streamConfig *StreamConfig) StderrPipe() io.ReadCloser { 317 reader, writer := io.Pipe() 318 streamConfig.stderr.AddWriter(writer, "") 319 return ioutils.NewBufReader(reader) 320 } 321 322 func (streamConfig *StreamConfig) StdoutLogPipe() io.ReadCloser { 323 reader, writer := io.Pipe() 324 streamConfig.stdout.AddWriter(writer, "stdout") 325 return ioutils.NewBufReader(reader) 326 } 327 328 func (streamConfig *StreamConfig) StderrLogPipe() io.ReadCloser { 329 reader, writer := io.Pipe() 330 streamConfig.stderr.AddWriter(writer, "stderr") 331 return ioutils.NewBufReader(reader) 332 } 333 334 func (container *Container) isNetworkAllocated() bool { 335 return container.NetworkSettings.IPAddress != "" 336 } 337 338 // cleanup releases any network resources allocated to the container along with any rules 339 // around how containers are linked together. It also unmounts the container's root filesystem. 340 func (container *Container) cleanup() { 341 container.ReleaseNetwork() 342 343 disableAllActiveLinks(container) 344 345 if err := container.Unmount(); err != nil { 346 logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err) 347 } 348 349 for _, eConfig := range container.execCommands.s { 350 container.daemon.unregisterExecCommand(eConfig) 351 } 352 353 container.UnmountVolumes(false) 354 } 355 356 func (container *Container) KillSig(sig int) error { 357 logrus.Debugf("Sending %d to %s", sig, container.ID) 358 container.Lock() 359 defer container.Unlock() 360 361 // We could unpause the container for them rather than returning this error 362 if container.Paused { 363 return fmt.Errorf("Container %s is paused. Unpause the container before stopping", container.ID) 364 } 365 366 if !container.Running { 367 return nil 368 } 369 370 // signal to the monitor that it should not restart the container 371 // after we send the kill signal 372 container.monitor.ExitOnNext() 373 374 // if the container is currently restarting we do not need to send the signal 375 // to the process. Telling the monitor that it should exit on it's next event 376 // loop is enough 377 if container.Restarting { 378 return nil 379 } 380 381 return container.daemon.Kill(container, sig) 382 } 383 384 // Wrapper aroung KillSig() suppressing "no such process" error. 385 func (container *Container) killPossiblyDeadProcess(sig int) error { 386 err := container.KillSig(sig) 387 if err == syscall.ESRCH { 388 logrus.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.GetPid(), sig) 389 return nil 390 } 391 return err 392 } 393 394 func (container *Container) Pause() error { 395 container.Lock() 396 defer container.Unlock() 397 398 // We cannot Pause the container which is already paused 399 if container.Paused { 400 return fmt.Errorf("Container %s is already paused", container.ID) 401 } 402 403 // We cannot Pause the container which is not running 404 if !container.Running { 405 return fmt.Errorf("Container %s is not running", container.ID) 406 } 407 408 if err := container.daemon.execDriver.Pause(container.command); err != nil { 409 return err 410 } 411 container.Paused = true 412 return nil 413 } 414 415 func (container *Container) Unpause() error { 416 container.Lock() 417 defer container.Unlock() 418 419 // We cannot unpause the container which is not paused 420 if !container.Paused { 421 return fmt.Errorf("Container %s is not paused, so what", container.ID) 422 } 423 424 // We cannot unpause the container which is not running 425 if !container.Running { 426 return fmt.Errorf("Container %s is not running", container.ID) 427 } 428 429 if err := container.daemon.execDriver.Unpause(container.command); err != nil { 430 return err 431 } 432 container.Paused = false 433 return nil 434 } 435 436 func (container *Container) Kill() error { 437 if !container.IsRunning() { 438 return nil 439 } 440 441 // 1. Send SIGKILL 442 if err := container.killPossiblyDeadProcess(9); err != nil { 443 // While normally we might "return err" here we're not going to 444 // because if we can't stop the container by this point then 445 // its probably because its already stopped. Meaning, between 446 // the time of the IsRunning() call above and now it stopped. 447 // Also, since the err return will be exec driver specific we can't 448 // look for any particular (common) error that would indicate 449 // that the process is already dead vs something else going wrong. 450 // So, instead we'll give it up to 2 more seconds to complete and if 451 // by that time the container is still running, then the error 452 // we got is probably valid and so we return it to the caller. 453 454 if container.IsRunning() { 455 container.WaitStop(2 * time.Second) 456 if container.IsRunning() { 457 return err 458 } 459 } 460 } 461 462 // 2. Wait for the process to die, in last resort, try to kill the process directly 463 if err := killProcessDirectly(container); err != nil { 464 return err 465 } 466 467 container.WaitStop(-1 * time.Second) 468 return nil 469 } 470 471 func (container *Container) Stop(seconds int) error { 472 if !container.IsRunning() { 473 return nil 474 } 475 476 // 1. Send a SIGTERM 477 if err := container.killPossiblyDeadProcess(15); err != nil { 478 logrus.Infof("Failed to send SIGTERM to the process, force killing") 479 if err := container.killPossiblyDeadProcess(9); err != nil { 480 return err 481 } 482 } 483 484 // 2. Wait for the process to exit on its own 485 if _, err := container.WaitStop(time.Duration(seconds) * time.Second); err != nil { 486 logrus.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds) 487 // 3. If it doesn't, then send SIGKILL 488 if err := container.Kill(); err != nil { 489 container.WaitStop(-1 * time.Second) 490 return err 491 } 492 } 493 494 return nil 495 } 496 497 func (container *Container) Restart(seconds int) error { 498 // Avoid unnecessarily unmounting and then directly mounting 499 // the container when the container stops and then starts 500 // again 501 if err := container.Mount(); err == nil { 502 defer container.Unmount() 503 } 504 505 if err := container.Stop(seconds); err != nil { 506 return err 507 } 508 return container.Start() 509 } 510 511 func (container *Container) Resize(h, w int) error { 512 if !container.IsRunning() { 513 return fmt.Errorf("Cannot resize container %s, container is not running", container.ID) 514 } 515 return container.command.ProcessConfig.Terminal.Resize(h, w) 516 } 517 518 func (container *Container) Export() (archive.Archive, error) { 519 if err := container.Mount(); err != nil { 520 return nil, err 521 } 522 523 archive, err := archive.Tar(container.basefs, archive.Uncompressed) 524 if err != nil { 525 container.Unmount() 526 return nil, err 527 } 528 return ioutils.NewReadCloserWrapper(archive, func() error { 529 err := archive.Close() 530 container.Unmount() 531 return err 532 }), 533 nil 534 } 535 536 func (container *Container) Mount() error { 537 return container.daemon.Mount(container) 538 } 539 540 func (container *Container) changes() ([]archive.Change, error) { 541 return container.daemon.Changes(container) 542 } 543 544 func (container *Container) Changes() ([]archive.Change, error) { 545 container.Lock() 546 defer container.Unlock() 547 return container.changes() 548 } 549 550 func (container *Container) GetImage() (*image.Image, error) { 551 if container.daemon == nil { 552 return nil, fmt.Errorf("Can't get image of unregistered container") 553 } 554 return container.daemon.graph.Get(container.ImageID) 555 } 556 557 func (container *Container) Unmount() error { 558 return container.daemon.Unmount(container) 559 } 560 561 func (container *Container) hostConfigPath() (string, error) { 562 return container.GetRootResourcePath("hostconfig.json") 563 } 564 565 func (container *Container) jsonPath() (string, error) { 566 return container.GetRootResourcePath("config.json") 567 } 568 569 // This method must be exported to be used from the lxc template 570 // This directory is only usable when the container is running 571 func (container *Container) RootfsPath() string { 572 return container.basefs 573 } 574 575 func validateID(id string) error { 576 if id == "" { 577 return fmt.Errorf("Invalid empty id") 578 } 579 return nil 580 } 581 582 func (container *Container) Copy(resource string) (io.ReadCloser, error) { 583 container.Lock() 584 defer container.Unlock() 585 var err error 586 if err := container.Mount(); err != nil { 587 return nil, err 588 } 589 defer func() { 590 if err != nil { 591 // unmount any volumes 592 container.UnmountVolumes(true) 593 // unmount the container's rootfs 594 container.Unmount() 595 } 596 }() 597 mounts, err := container.setupMounts() 598 if err != nil { 599 return nil, err 600 } 601 for _, m := range mounts { 602 dest, err := container.GetResourcePath(m.Destination) 603 if err != nil { 604 return nil, err 605 } 606 if err := mount.Mount(m.Source, dest, "bind", "rbind,ro"); err != nil { 607 return nil, err 608 } 609 } 610 basePath, err := container.GetResourcePath(resource) 611 if err != nil { 612 return nil, err 613 } 614 stat, err := os.Stat(basePath) 615 if err != nil { 616 return nil, err 617 } 618 var filter []string 619 if !stat.IsDir() { 620 d, f := filepath.Split(basePath) 621 basePath = d 622 filter = []string{f} 623 } else { 624 filter = []string{filepath.Base(basePath)} 625 basePath = filepath.Dir(basePath) 626 } 627 archive, err := archive.TarWithOptions(basePath, &archive.TarOptions{ 628 Compression: archive.Uncompressed, 629 IncludeFiles: filter, 630 }) 631 if err != nil { 632 return nil, err 633 } 634 return ioutils.NewReadCloserWrapper(archive, func() error { 635 err := archive.Close() 636 container.UnmountVolumes(true) 637 container.Unmount() 638 return err 639 }), 640 nil 641 } 642 643 // Returns true if the container exposes a certain port 644 func (container *Container) Exposes(p nat.Port) bool { 645 _, exists := container.Config.ExposedPorts[p] 646 return exists 647 } 648 649 func (container *Container) HostConfig() *runconfig.HostConfig { 650 return container.hostConfig 651 } 652 653 func (container *Container) SetHostConfig(hostConfig *runconfig.HostConfig) { 654 container.hostConfig = hostConfig 655 } 656 657 func (container *Container) getLogConfig() runconfig.LogConfig { 658 cfg := container.hostConfig.LogConfig 659 if cfg.Type != "" { // container has log driver configured 660 return cfg 661 } 662 // Use daemon's default log config for containers 663 return container.daemon.defaultLogConfig 664 } 665 666 func (container *Container) getLogger() (logger.Logger, error) { 667 cfg := container.getLogConfig() 668 c, err := logger.GetLogDriver(cfg.Type) 669 if err != nil { 670 return nil, fmt.Errorf("Failed to get logging factory: %v", err) 671 } 672 ctx := logger.Context{ 673 Config: cfg.Config, 674 ContainerID: container.ID, 675 ContainerName: container.Name, 676 } 677 678 // Set logging file for "json-logger" 679 if cfg.Type == jsonfilelog.Name { 680 ctx.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID)) 681 if err != nil { 682 return nil, err 683 } 684 } 685 return c(ctx) 686 } 687 688 func (container *Container) startLogging() error { 689 cfg := container.getLogConfig() 690 if cfg.Type == "none" { 691 return nil // do not start logging routines 692 } 693 694 l, err := container.getLogger() 695 if err != nil { 696 return fmt.Errorf("Failed to initialize logging driver: %v", err) 697 } 698 699 copier, err := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) 700 if err != nil { 701 return err 702 } 703 container.logCopier = copier 704 copier.Run() 705 container.logDriver = l 706 707 // set LogPath field only for json-file logdriver 708 if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok { 709 container.LogPath = jl.LogPath() 710 } 711 712 return nil 713 } 714 715 func (container *Container) waitForStart() error { 716 container.monitor = newContainerMonitor(container, container.hostConfig.RestartPolicy) 717 718 // block until we either receive an error from the initial start of the container's 719 // process or until the process is running in the container 720 select { 721 case <-container.monitor.startSignal: 722 case err := <-promise.Go(container.monitor.Start): 723 return err 724 } 725 726 return nil 727 } 728 729 func (container *Container) GetProcessLabel() string { 730 // even if we have a process label return "" if we are running 731 // in privileged mode 732 if container.hostConfig.Privileged { 733 return "" 734 } 735 return container.ProcessLabel 736 } 737 738 func (container *Container) GetMountLabel() string { 739 if container.hostConfig.Privileged { 740 return "" 741 } 742 return container.MountLabel 743 } 744 745 func (container *Container) Stats() (*execdriver.ResourceStats, error) { 746 return container.daemon.Stats(container) 747 } 748 749 func (c *Container) LogDriverType() string { 750 c.Lock() 751 defer c.Unlock() 752 if c.hostConfig.LogConfig.Type == "" { 753 return c.daemon.defaultLogConfig.Type 754 } 755 return c.hostConfig.LogConfig.Type 756 } 757 758 func (container *Container) GetExecIDs() []string { 759 return container.execCommands.List() 760 } 761 762 func (container *Container) Exec(execConfig *execConfig) error { 763 container.Lock() 764 defer container.Unlock() 765 766 waitStart := make(chan struct{}) 767 768 callback := func(processConfig *execdriver.ProcessConfig, pid int) { 769 if processConfig.Tty { 770 // The callback is called after the process Start() 771 // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave 772 // which we close here. 773 if c, ok := processConfig.Stdout.(io.Closer); ok { 774 c.Close() 775 } 776 } 777 close(waitStart) 778 } 779 780 // We use a callback here instead of a goroutine and an chan for 781 // syncronization purposes 782 cErr := promise.Go(func() error { return container.monitorExec(execConfig, callback) }) 783 784 // Exec should not return until the process is actually running 785 select { 786 case <-waitStart: 787 case err := <-cErr: 788 return err 789 } 790 791 return nil 792 } 793 794 func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) error { 795 var ( 796 err error 797 exitCode int 798 ) 799 800 pipes := execdriver.NewPipes(execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdout, execConfig.StreamConfig.stderr, execConfig.OpenStdin) 801 exitCode, err = container.daemon.Exec(container, execConfig, pipes, callback) 802 if err != nil { 803 logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) 804 } 805 806 logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) 807 if execConfig.OpenStdin { 808 if err := execConfig.StreamConfig.stdin.Close(); err != nil { 809 logrus.Errorf("Error closing stdin while running in %s: %s", container.ID, err) 810 } 811 } 812 if err := execConfig.StreamConfig.stdout.Clean(); err != nil { 813 logrus.Errorf("Error closing stdout while running in %s: %s", container.ID, err) 814 } 815 if err := execConfig.StreamConfig.stderr.Clean(); err != nil { 816 logrus.Errorf("Error closing stderr while running in %s: %s", container.ID, err) 817 } 818 if execConfig.ProcessConfig.Terminal != nil { 819 if err := execConfig.ProcessConfig.Terminal.Close(); err != nil { 820 logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) 821 } 822 } 823 824 return err 825 } 826 827 func (c *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error { 828 return attach(&c.StreamConfig, c.Config.OpenStdin, c.Config.StdinOnce, c.Config.Tty, stdin, stdout, stderr) 829 } 830 831 func (c *Container) AttachWithLogs(stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error { 832 if logs { 833 logDriver, err := c.getLogger() 834 cLog, err := logDriver.GetReader() 835 836 if err != nil { 837 logrus.Errorf("Error reading logs: %s", err) 838 } else if c.LogDriverType() != jsonfilelog.Name { 839 logrus.Errorf("Reading logs not implemented for driver %s", c.LogDriverType()) 840 } else { 841 dec := json.NewDecoder(cLog) 842 for { 843 l := &jsonlog.JSONLog{} 844 845 if err := dec.Decode(l); err == io.EOF { 846 break 847 } else if err != nil { 848 logrus.Errorf("Error streaming logs: %s", err) 849 break 850 } 851 if l.Stream == "stdout" && stdout != nil { 852 io.WriteString(stdout, l.Log) 853 } 854 if l.Stream == "stderr" && stderr != nil { 855 io.WriteString(stderr, l.Log) 856 } 857 } 858 } 859 } 860 861 //stream 862 if stream { 863 var stdinPipe io.ReadCloser 864 if stdin != nil { 865 r, w := io.Pipe() 866 go func() { 867 defer w.Close() 868 defer logrus.Debugf("Closing buffered stdin pipe") 869 io.Copy(w, stdin) 870 }() 871 stdinPipe = r 872 } 873 <-c.Attach(stdinPipe, stdout, stderr) 874 // If we are in stdinonce mode, wait for the process to end 875 // otherwise, simply return 876 if c.Config.StdinOnce && !c.Config.Tty { 877 c.WaitStop(-1 * time.Second) 878 } 879 } 880 return nil 881 } 882 883 func attach(streamConfig *StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error { 884 var ( 885 cStdout, cStderr io.ReadCloser 886 cStdin io.WriteCloser 887 wg sync.WaitGroup 888 errors = make(chan error, 3) 889 ) 890 891 if stdin != nil && openStdin { 892 cStdin = streamConfig.StdinPipe() 893 wg.Add(1) 894 } 895 896 if stdout != nil { 897 cStdout = streamConfig.StdoutPipe() 898 wg.Add(1) 899 } 900 901 if stderr != nil { 902 cStderr = streamConfig.StderrPipe() 903 wg.Add(1) 904 } 905 906 // Connect stdin of container to the http conn. 907 go func() { 908 if stdin == nil || !openStdin { 909 return 910 } 911 logrus.Debugf("attach: stdin: begin") 912 defer func() { 913 if stdinOnce && !tty { 914 cStdin.Close() 915 } else { 916 // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr 917 if cStdout != nil { 918 cStdout.Close() 919 } 920 if cStderr != nil { 921 cStderr.Close() 922 } 923 } 924 wg.Done() 925 logrus.Debugf("attach: stdin: end") 926 }() 927 928 var err error 929 if tty { 930 _, err = copyEscapable(cStdin, stdin) 931 } else { 932 _, err = io.Copy(cStdin, stdin) 933 934 } 935 if err == io.ErrClosedPipe { 936 err = nil 937 } 938 if err != nil { 939 logrus.Errorf("attach: stdin: %s", err) 940 errors <- err 941 return 942 } 943 }() 944 945 attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) { 946 if stream == nil { 947 return 948 } 949 defer func() { 950 // Make sure stdin gets closed 951 if stdin != nil { 952 stdin.Close() 953 } 954 streamPipe.Close() 955 wg.Done() 956 logrus.Debugf("attach: %s: end", name) 957 }() 958 959 logrus.Debugf("attach: %s: begin", name) 960 _, err := io.Copy(stream, streamPipe) 961 if err == io.ErrClosedPipe { 962 err = nil 963 } 964 if err != nil { 965 logrus.Errorf("attach: %s: %v", name, err) 966 errors <- err 967 } 968 } 969 970 go attachStream("stdout", stdout, cStdout) 971 go attachStream("stderr", stderr, cStderr) 972 973 return promise.Go(func() error { 974 wg.Wait() 975 close(errors) 976 for err := range errors { 977 if err != nil { 978 return err 979 } 980 } 981 return nil 982 }) 983 } 984 985 // Code c/c from io.Copy() modified to handle escape sequence 986 func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) { 987 buf := make([]byte, 32*1024) 988 for { 989 nr, er := src.Read(buf) 990 if nr > 0 { 991 // ---- Docker addition 992 // char 16 is C-p 993 if nr == 1 && buf[0] == 16 { 994 nr, er = src.Read(buf) 995 // char 17 is C-q 996 if nr == 1 && buf[0] == 17 { 997 if err := src.Close(); err != nil { 998 return 0, err 999 } 1000 return 0, nil 1001 } 1002 } 1003 // ---- End of docker 1004 nw, ew := dst.Write(buf[0:nr]) 1005 if nw > 0 { 1006 written += int64(nw) 1007 } 1008 if ew != nil { 1009 err = ew 1010 break 1011 } 1012 if nr != nw { 1013 err = io.ErrShortWrite 1014 break 1015 } 1016 } 1017 if er == io.EOF { 1018 break 1019 } 1020 if er != nil { 1021 err = er 1022 break 1023 } 1024 } 1025 return written, err 1026 } 1027 1028 func (container *Container) networkMounts() []execdriver.Mount { 1029 var mounts []execdriver.Mount 1030 if container.ResolvConfPath != "" { 1031 label.SetFileLabel(container.ResolvConfPath, container.MountLabel) 1032 mounts = append(mounts, execdriver.Mount{ 1033 Source: container.ResolvConfPath, 1034 Destination: "/etc/resolv.conf", 1035 Writable: !container.hostConfig.ReadonlyRootfs, 1036 Private: true, 1037 }) 1038 } 1039 if container.HostnamePath != "" { 1040 label.SetFileLabel(container.HostnamePath, container.MountLabel) 1041 mounts = append(mounts, execdriver.Mount{ 1042 Source: container.HostnamePath, 1043 Destination: "/etc/hostname", 1044 Writable: !container.hostConfig.ReadonlyRootfs, 1045 Private: true, 1046 }) 1047 } 1048 if container.HostsPath != "" { 1049 label.SetFileLabel(container.HostsPath, container.MountLabel) 1050 mounts = append(mounts, execdriver.Mount{ 1051 Source: container.HostsPath, 1052 Destination: "/etc/hosts", 1053 Writable: !container.hostConfig.ReadonlyRootfs, 1054 Private: true, 1055 }) 1056 } 1057 return mounts 1058 } 1059 1060 func (container *Container) addBindMountPoint(name, source, destination string, rw bool) { 1061 container.MountPoints[destination] = &mountPoint{ 1062 Name: name, 1063 Source: source, 1064 Destination: destination, 1065 RW: rw, 1066 } 1067 } 1068 1069 func (container *Container) addLocalMountPoint(name, destination string, rw bool) { 1070 container.MountPoints[destination] = &mountPoint{ 1071 Name: name, 1072 Driver: volume.DefaultDriverName, 1073 Destination: destination, 1074 RW: rw, 1075 } 1076 } 1077 1078 func (container *Container) addMountPointWithVolume(destination string, vol volume.Volume, rw bool) { 1079 container.MountPoints[destination] = &mountPoint{ 1080 Name: vol.Name(), 1081 Driver: vol.DriverName(), 1082 Destination: destination, 1083 RW: rw, 1084 Volume: vol, 1085 } 1086 } 1087 1088 func (container *Container) isDestinationMounted(destination string) bool { 1089 return container.MountPoints[destination] != nil 1090 } 1091 1092 func (container *Container) prepareMountPoints() error { 1093 for _, config := range container.MountPoints { 1094 if len(config.Driver) > 0 { 1095 v, err := createVolume(config.Name, config.Driver) 1096 if err != nil { 1097 return err 1098 } 1099 config.Volume = v 1100 } 1101 } 1102 return nil 1103 } 1104 1105 func (container *Container) removeMountPoints() error { 1106 for _, m := range container.MountPoints { 1107 if m.Volume != nil { 1108 if err := removeVolume(m.Volume); err != nil { 1109 return err 1110 } 1111 } 1112 } 1113 return nil 1114 } 1115 1116 func (container *Container) shouldRestart() bool { 1117 return container.hostConfig.RestartPolicy.Name == "always" || 1118 (container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0) 1119 } 1120 1121 func (container *Container) copyImagePathContent(v volume.Volume, destination string) error { 1122 rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, destination), container.basefs) 1123 if err != nil { 1124 return err 1125 } 1126 1127 if _, err = ioutil.ReadDir(rootfs); err != nil { 1128 if os.IsNotExist(err) { 1129 return nil 1130 } 1131 return err 1132 } 1133 1134 path, err := v.Mount() 1135 if err != nil { 1136 return err 1137 } 1138 1139 if err := copyExistingContents(rootfs, path); err != nil { 1140 return err 1141 } 1142 1143 return v.Unmount() 1144 }