github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/daemon/container.go (about) 1 package daemon 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path" 12 "path/filepath" 13 "strings" 14 "syscall" 15 "time" 16 17 "github.com/docker/libcontainer/configs" 18 "github.com/docker/libcontainer/devices" 19 "github.com/docker/libcontainer/label" 20 21 "github.com/Sirupsen/logrus" 22 "github.com/docker/docker/daemon/execdriver" 23 "github.com/docker/docker/daemon/logger" 24 "github.com/docker/docker/daemon/logger/journald" 25 "github.com/docker/docker/daemon/logger/jsonfilelog" 26 "github.com/docker/docker/daemon/logger/syslog" 27 "github.com/docker/docker/daemon/network" 28 "github.com/docker/docker/daemon/networkdriver/bridge" 29 "github.com/docker/docker/engine" 30 "github.com/docker/docker/image" 31 "github.com/docker/docker/links" 32 "github.com/docker/docker/nat" 33 "github.com/docker/docker/pkg/archive" 34 "github.com/docker/docker/pkg/broadcastwriter" 35 "github.com/docker/docker/pkg/directory" 36 "github.com/docker/docker/pkg/etchosts" 37 "github.com/docker/docker/pkg/ioutils" 38 "github.com/docker/docker/pkg/promise" 39 "github.com/docker/docker/pkg/resolvconf" 40 "github.com/docker/docker/pkg/stringid" 41 "github.com/docker/docker/pkg/symlink" 42 "github.com/docker/docker/pkg/ulimit" 43 "github.com/docker/docker/runconfig" 44 "github.com/docker/docker/utils" 45 ) 46 47 const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 48 49 var ( 50 ErrNotATTY = errors.New("The PTY is not a file") 51 ErrNoTTY = errors.New("No PTY found") 52 ErrContainerStart = errors.New("The container failed to start. Unknown error") 53 ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.") 54 ) 55 56 type StreamConfig struct { 57 stdout *broadcastwriter.BroadcastWriter 58 stderr *broadcastwriter.BroadcastWriter 59 stdin io.ReadCloser 60 stdinPipe io.WriteCloser 61 } 62 63 type Container struct { 64 *State `json:"State"` // Needed for remote api version <= 1.11 65 root string // Path to the "home" of the container, including metadata. 66 basefs string // Path to the graphdriver mountpoint 67 68 ID string 69 70 Created time.Time 71 72 Path string 73 Args []string 74 75 Config *runconfig.Config 76 ImageID string `json:"Image"` 77 78 NetworkSettings *network.Settings 79 80 ResolvConfPath string 81 HostnamePath string 82 HostsPath string 83 LogPath string 84 Name string 85 Driver string 86 ExecDriver string 87 88 command *execdriver.Command 89 StreamConfig 90 91 daemon *Daemon 92 MountLabel, ProcessLabel string 93 AppArmorProfile string 94 RestartCount int 95 UpdateDns bool 96 97 // Maps container paths to volume paths. The key in this is the path to which 98 // the volume is being mounted inside the container. Value is the path of the 99 // volume on disk 100 Volumes map[string]string 101 // Store rw/ro in a separate structure to preserve reverse-compatibility on-disk. 102 // Easier than migrating older container configs :) 103 VolumesRW map[string]bool 104 hostConfig *runconfig.HostConfig 105 106 activeLinks map[string]*links.Link 107 monitor *containerMonitor 108 execCommands *execStore 109 // logDriver for closing 110 logDriver logger.Logger 111 logCopier *logger.Copier 112 AppliedVolumesFrom map[string]struct{} 113 } 114 115 func (container *Container) FromDisk() error { 116 pth, err := container.jsonPath() 117 if err != nil { 118 return err 119 } 120 121 jsonSource, err := os.Open(pth) 122 if err != nil { 123 return err 124 } 125 defer jsonSource.Close() 126 127 dec := json.NewDecoder(jsonSource) 128 129 // Load container settings 130 // udp broke compat of docker.PortMapping, but it's not used when loading a container, we can skip it 131 if err := dec.Decode(container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") { 132 return err 133 } 134 135 if err := label.ReserveLabel(container.ProcessLabel); err != nil { 136 return err 137 } 138 return container.readHostConfig() 139 } 140 141 func (container *Container) toDisk() error { 142 data, err := json.Marshal(container) 143 if err != nil { 144 return err 145 } 146 147 pth, err := container.jsonPath() 148 if err != nil { 149 return err 150 } 151 152 if err := ioutil.WriteFile(pth, data, 0666); err != nil { 153 return err 154 } 155 156 return container.WriteHostConfig() 157 } 158 159 func (container *Container) ToDisk() error { 160 container.Lock() 161 err := container.toDisk() 162 container.Unlock() 163 return err 164 } 165 166 func (container *Container) readHostConfig() error { 167 container.hostConfig = &runconfig.HostConfig{} 168 // If the hostconfig file does not exist, do not read it. 169 // (We still have to initialize container.hostConfig, 170 // but that's OK, since we just did that above.) 171 pth, err := container.hostConfigPath() 172 if err != nil { 173 return err 174 } 175 176 _, err = os.Stat(pth) 177 if os.IsNotExist(err) { 178 return nil 179 } 180 181 f, err := os.Open(pth) 182 if err != nil { 183 return err 184 } 185 defer f.Close() 186 187 return json.NewDecoder(f).Decode(&container.hostConfig) 188 } 189 190 func (container *Container) WriteHostConfig() error { 191 data, err := json.Marshal(container.hostConfig) 192 if err != nil { 193 return err 194 } 195 196 pth, err := container.hostConfigPath() 197 if err != nil { 198 return err 199 } 200 201 return ioutil.WriteFile(pth, data, 0666) 202 } 203 204 func (container *Container) LogEvent(action string) { 205 d := container.daemon 206 d.EventsService.Log( 207 action, 208 container.ID, 209 container.Config.Image, 210 ) 211 } 212 213 func (container *Container) getResourcePath(path string) (string, error) { 214 cleanPath := filepath.Join("/", path) 215 return symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs) 216 } 217 218 func (container *Container) getRootResourcePath(path string) (string, error) { 219 cleanPath := filepath.Join("/", path) 220 return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root) 221 } 222 223 func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs.Device, err error) { 224 device, err := devices.DeviceFromPath(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions) 225 // if there was no error, return the device 226 if err == nil { 227 device.Path = deviceMapping.PathInContainer 228 return append(devs, device), nil 229 } 230 231 // if the device is not a device node 232 // try to see if it's a directory holding many devices 233 if err == devices.ErrNotADevice { 234 235 // check if it is a directory 236 if src, e := os.Stat(deviceMapping.PathOnHost); e == nil && src.IsDir() { 237 238 // mount the internal devices recursively 239 filepath.Walk(deviceMapping.PathOnHost, func(dpath string, f os.FileInfo, e error) error { 240 childDevice, e := devices.DeviceFromPath(dpath, deviceMapping.CgroupPermissions) 241 if e != nil { 242 // ignore the device 243 return nil 244 } 245 246 // add the device to userSpecified devices 247 childDevice.Path = strings.Replace(dpath, deviceMapping.PathOnHost, deviceMapping.PathInContainer, 1) 248 devs = append(devs, childDevice) 249 250 return nil 251 }) 252 } 253 } 254 255 if len(devs) > 0 { 256 return devs, nil 257 } 258 259 return devs, fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err) 260 } 261 262 func populateCommand(c *Container, env []string) error { 263 en := &execdriver.Network{ 264 Mtu: c.daemon.config.Mtu, 265 Interface: nil, 266 } 267 268 parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2) 269 switch parts[0] { 270 case "none": 271 case "host": 272 en.HostNetworking = true 273 case "bridge", "": // empty string to support existing containers 274 if !c.Config.NetworkDisabled { 275 network := c.NetworkSettings 276 en.Interface = &execdriver.NetworkInterface{ 277 Gateway: network.Gateway, 278 Bridge: network.Bridge, 279 IPAddress: network.IPAddress, 280 IPPrefixLen: network.IPPrefixLen, 281 MacAddress: network.MacAddress, 282 LinkLocalIPv6Address: network.LinkLocalIPv6Address, 283 GlobalIPv6Address: network.GlobalIPv6Address, 284 GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen, 285 IPv6Gateway: network.IPv6Gateway, 286 } 287 } 288 case "container": 289 nc, err := c.getNetworkedContainer() 290 if err != nil { 291 return err 292 } 293 en.ContainerID = nc.ID 294 default: 295 return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode) 296 } 297 298 ipc := &execdriver.Ipc{} 299 300 if c.hostConfig.IpcMode.IsContainer() { 301 ic, err := c.getIpcContainer() 302 if err != nil { 303 return err 304 } 305 ipc.ContainerID = ic.ID 306 } else { 307 ipc.HostIpc = c.hostConfig.IpcMode.IsHost() 308 } 309 310 pid := &execdriver.Pid{} 311 pid.HostPid = c.hostConfig.PidMode.IsHost() 312 313 // Build lists of devices allowed and created within the container. 314 var userSpecifiedDevices []*configs.Device 315 for _, deviceMapping := range c.hostConfig.Devices { 316 devs, err := getDevicesFromPath(deviceMapping) 317 if err != nil { 318 return err 319 } 320 321 userSpecifiedDevices = append(userSpecifiedDevices, devs...) 322 } 323 allowedDevices := append(configs.DefaultAllowedDevices, userSpecifiedDevices...) 324 325 autoCreatedDevices := append(configs.DefaultAutoCreatedDevices, userSpecifiedDevices...) 326 327 // TODO: this can be removed after lxc-conf is fully deprecated 328 lxcConfig, err := mergeLxcConfIntoOptions(c.hostConfig) 329 if err != nil { 330 return err 331 } 332 333 var rlimits []*ulimit.Rlimit 334 ulimits := c.hostConfig.Ulimits 335 336 // Merge ulimits with daemon defaults 337 ulIdx := make(map[string]*ulimit.Ulimit) 338 for _, ul := range ulimits { 339 ulIdx[ul.Name] = ul 340 } 341 for name, ul := range c.daemon.config.Ulimits { 342 if _, exists := ulIdx[name]; !exists { 343 ulimits = append(ulimits, ul) 344 } 345 } 346 347 for _, limit := range ulimits { 348 rl, err := limit.GetRlimit() 349 if err != nil { 350 return err 351 } 352 rlimits = append(rlimits, rl) 353 } 354 355 resources := &execdriver.Resources{ 356 Memory: c.hostConfig.Memory, 357 MemorySwap: c.hostConfig.MemorySwap, 358 CpuShares: c.hostConfig.CpuShares, 359 CpusetCpus: c.hostConfig.CpusetCpus, 360 CpusetMems: c.hostConfig.CpusetMems, 361 CpuQuota: c.hostConfig.CpuQuota, 362 Rlimits: rlimits, 363 } 364 365 processConfig := execdriver.ProcessConfig{ 366 Privileged: c.hostConfig.Privileged, 367 Entrypoint: c.Path, 368 Arguments: c.Args, 369 Tty: c.Config.Tty, 370 User: c.Config.User, 371 } 372 373 processConfig.SysProcAttr = &syscall.SysProcAttr{Setsid: true} 374 processConfig.Env = env 375 376 c.command = &execdriver.Command{ 377 ID: c.ID, 378 Rootfs: c.RootfsPath(), 379 ReadonlyRootfs: c.hostConfig.ReadonlyRootfs, 380 InitPath: "/.dockerinit", 381 WorkingDir: c.Config.WorkingDir, 382 Network: en, 383 Ipc: ipc, 384 Pid: pid, 385 Resources: resources, 386 AllowedDevices: allowedDevices, 387 AutoCreatedDevices: autoCreatedDevices, 388 CapAdd: c.hostConfig.CapAdd, 389 CapDrop: c.hostConfig.CapDrop, 390 ProcessConfig: processConfig, 391 ProcessLabel: c.GetProcessLabel(), 392 MountLabel: c.GetMountLabel(), 393 LxcConfig: lxcConfig, 394 AppArmorProfile: c.AppArmorProfile, 395 CgroupParent: c.hostConfig.CgroupParent, 396 } 397 398 return nil 399 } 400 401 func (container *Container) Start() (err error) { 402 container.Lock() 403 defer container.Unlock() 404 405 if container.Running { 406 return nil 407 } 408 409 if container.removalInProgress || container.Dead { 410 return fmt.Errorf("Container is marked for removal and cannot be started.") 411 } 412 413 // if we encounter an error during start we need to ensure that any other 414 // setup has been cleaned up properly 415 defer func() { 416 if err != nil { 417 container.setError(err) 418 // if no one else has set it, make sure we don't leave it at zero 419 if container.ExitCode == 0 { 420 container.ExitCode = 128 421 } 422 container.toDisk() 423 container.cleanup() 424 } 425 }() 426 427 if err := container.setupContainerDns(); err != nil { 428 return err 429 } 430 if err := container.Mount(); err != nil { 431 return err 432 } 433 if err := container.initializeNetworking(); err != nil { 434 return err 435 } 436 if err := container.updateParentsHosts(); err != nil { 437 return err 438 } 439 container.verifyDaemonSettings() 440 if err := container.prepareVolumes(); err != nil { 441 return err 442 } 443 linkedEnv, err := container.setupLinkedContainers() 444 if err != nil { 445 return err 446 } 447 if err := container.setupWorkingDirectory(); err != nil { 448 return err 449 } 450 env := container.createDaemonEnvironment(linkedEnv) 451 if err := populateCommand(container, env); err != nil { 452 return err 453 } 454 if err := container.setupMounts(); err != nil { 455 return err 456 } 457 458 return container.waitForStart() 459 } 460 461 func (container *Container) Run() error { 462 if err := container.Start(); err != nil { 463 return err 464 } 465 container.WaitStop(-1 * time.Second) 466 return nil 467 } 468 469 func (container *Container) Output() (output []byte, err error) { 470 pipe := container.StdoutPipe() 471 defer pipe.Close() 472 if err := container.Start(); err != nil { 473 return nil, err 474 } 475 output, err = ioutil.ReadAll(pipe) 476 container.WaitStop(-1 * time.Second) 477 return output, err 478 } 479 480 // StreamConfig.StdinPipe returns a WriteCloser which can be used to feed data 481 // to the standard input of the container's active process. 482 // Container.StdoutPipe and Container.StderrPipe each return a ReadCloser 483 // which can be used to retrieve the standard output (and error) generated 484 // by the container's active process. The output (and error) are actually 485 // copied and delivered to all StdoutPipe and StderrPipe consumers, using 486 // a kind of "broadcaster". 487 488 func (streamConfig *StreamConfig) StdinPipe() io.WriteCloser { 489 return streamConfig.stdinPipe 490 } 491 492 func (streamConfig *StreamConfig) StdoutPipe() io.ReadCloser { 493 reader, writer := io.Pipe() 494 streamConfig.stdout.AddWriter(writer, "") 495 return ioutils.NewBufReader(reader) 496 } 497 498 func (streamConfig *StreamConfig) StderrPipe() io.ReadCloser { 499 reader, writer := io.Pipe() 500 streamConfig.stderr.AddWriter(writer, "") 501 return ioutils.NewBufReader(reader) 502 } 503 504 func (streamConfig *StreamConfig) StdoutLogPipe() io.ReadCloser { 505 reader, writer := io.Pipe() 506 streamConfig.stdout.AddWriter(writer, "stdout") 507 return ioutils.NewBufReader(reader) 508 } 509 510 func (streamConfig *StreamConfig) StderrLogPipe() io.ReadCloser { 511 reader, writer := io.Pipe() 512 streamConfig.stderr.AddWriter(writer, "stderr") 513 return ioutils.NewBufReader(reader) 514 } 515 516 func (container *Container) buildHostnameFile() error { 517 hostnamePath, err := container.getRootResourcePath("hostname") 518 if err != nil { 519 return err 520 } 521 container.HostnamePath = hostnamePath 522 523 if container.Config.Domainname != "" { 524 return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) 525 } 526 return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) 527 } 528 529 func (container *Container) buildHostsFiles(IP string) error { 530 531 hostsPath, err := container.getRootResourcePath("hosts") 532 if err != nil { 533 return err 534 } 535 container.HostsPath = hostsPath 536 537 var extraContent []etchosts.Record 538 539 children, err := container.daemon.Children(container.Name) 540 if err != nil { 541 return err 542 } 543 544 for linkAlias, child := range children { 545 _, alias := path.Split(linkAlias) 546 // allow access to the linked container via the alias, real name, and container hostname 547 aliasList := alias + " " + child.Config.Hostname 548 // only add the name if alias isn't equal to the name 549 if alias != child.Name[1:] { 550 aliasList = aliasList + " " + child.Name[1:] 551 } 552 extraContent = append(extraContent, etchosts.Record{Hosts: aliasList, IP: child.NetworkSettings.IPAddress}) 553 } 554 555 for _, extraHost := range container.hostConfig.ExtraHosts { 556 // allow IPv6 addresses in extra hosts; only split on first ":" 557 parts := strings.SplitN(extraHost, ":", 2) 558 extraContent = append(extraContent, etchosts.Record{Hosts: parts[0], IP: parts[1]}) 559 } 560 561 return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, extraContent) 562 } 563 564 func (container *Container) buildHostnameAndHostsFiles(IP string) error { 565 if err := container.buildHostnameFile(); err != nil { 566 return err 567 } 568 569 return container.buildHostsFiles(IP) 570 } 571 572 func (container *Container) AllocateNetwork() error { 573 mode := container.hostConfig.NetworkMode 574 if container.Config.NetworkDisabled || !mode.IsPrivate() { 575 return nil 576 } 577 578 var ( 579 err error 580 eng = container.daemon.eng 581 ) 582 583 networkSettings, err := bridge.Allocate(container.ID, container.Config.MacAddress, "", "") 584 if err != nil { 585 return err 586 } 587 588 // Error handling: At this point, the interface is allocated so we have to 589 // make sure that it is always released in case of error, otherwise we 590 // might leak resources. 591 592 if container.Config.PortSpecs != nil { 593 if err = migratePortMappings(container.Config, container.hostConfig); err != nil { 594 bridge.Release(container.ID) 595 return err 596 } 597 container.Config.PortSpecs = nil 598 if err = container.WriteHostConfig(); err != nil { 599 bridge.Release(container.ID) 600 return err 601 } 602 } 603 604 var ( 605 portSpecs = make(nat.PortSet) 606 bindings = make(nat.PortMap) 607 ) 608 609 if container.Config.ExposedPorts != nil { 610 portSpecs = container.Config.ExposedPorts 611 } 612 613 if container.hostConfig.PortBindings != nil { 614 for p, b := range container.hostConfig.PortBindings { 615 bindings[p] = []nat.PortBinding{} 616 for _, bb := range b { 617 bindings[p] = append(bindings[p], nat.PortBinding{ 618 HostIp: bb.HostIp, 619 HostPort: bb.HostPort, 620 }) 621 } 622 } 623 } 624 625 container.NetworkSettings.PortMapping = nil 626 627 for port := range portSpecs { 628 if err = container.allocatePort(eng, port, bindings); err != nil { 629 bridge.Release(container.ID) 630 return err 631 } 632 } 633 container.WriteHostConfig() 634 635 networkSettings.Ports = bindings 636 container.NetworkSettings = networkSettings 637 638 return nil 639 } 640 641 func (container *Container) ReleaseNetwork() { 642 if container.Config.NetworkDisabled || !container.hostConfig.NetworkMode.IsPrivate() { 643 return 644 } 645 646 bridge.Release(container.ID) 647 648 container.NetworkSettings = &network.Settings{} 649 } 650 651 func (container *Container) isNetworkAllocated() bool { 652 return container.NetworkSettings.IPAddress != "" 653 } 654 655 func (container *Container) RestoreNetwork() error { 656 mode := container.hostConfig.NetworkMode 657 // Don't attempt a restore if we previously didn't allocate networking. 658 // This might be a legacy container with no network allocated, in which case the 659 // allocation will happen once and for all at start. 660 if !container.isNetworkAllocated() || container.Config.NetworkDisabled || !mode.IsPrivate() { 661 return nil 662 } 663 664 eng := container.daemon.eng 665 666 // Re-allocate the interface with the same IP and MAC address. 667 if _, err := bridge.Allocate(container.ID, container.NetworkSettings.MacAddress, container.NetworkSettings.IPAddress, ""); err != nil { 668 return err 669 } 670 671 // Re-allocate any previously allocated ports. 672 for port := range container.NetworkSettings.Ports { 673 if err := container.allocatePort(eng, port, container.NetworkSettings.Ports); err != nil { 674 return err 675 } 676 } 677 return nil 678 } 679 680 // cleanup releases any network resources allocated to the container along with any rules 681 // around how containers are linked together. It also unmounts the container's root filesystem. 682 func (container *Container) cleanup() { 683 container.ReleaseNetwork() 684 685 // Disable all active links 686 if container.activeLinks != nil { 687 for _, link := range container.activeLinks { 688 link.Disable() 689 } 690 } 691 692 if err := container.Unmount(); err != nil { 693 logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err) 694 } 695 696 for _, eConfig := range container.execCommands.s { 697 container.daemon.unregisterExecCommand(eConfig) 698 } 699 } 700 701 func (container *Container) KillSig(sig int) error { 702 logrus.Debugf("Sending %d to %s", sig, container.ID) 703 container.Lock() 704 defer container.Unlock() 705 706 // We could unpause the container for them rather than returning this error 707 if container.Paused { 708 return fmt.Errorf("Container %s is paused. Unpause the container before stopping", container.ID) 709 } 710 711 if !container.Running { 712 return nil 713 } 714 715 // signal to the monitor that it should not restart the container 716 // after we send the kill signal 717 container.monitor.ExitOnNext() 718 719 // if the container is currently restarting we do not need to send the signal 720 // to the process. Telling the monitor that it should exit on it's next event 721 // loop is enough 722 if container.Restarting { 723 return nil 724 } 725 726 return container.daemon.Kill(container, sig) 727 } 728 729 // Wrapper aroung KillSig() suppressing "no such process" error. 730 func (container *Container) killPossiblyDeadProcess(sig int) error { 731 err := container.KillSig(sig) 732 if err == syscall.ESRCH { 733 logrus.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.GetPid(), sig) 734 return nil 735 } 736 return err 737 } 738 739 func (container *Container) Pause() error { 740 if container.IsPaused() { 741 return fmt.Errorf("Container %s is already paused", container.ID) 742 } 743 if !container.IsRunning() { 744 return fmt.Errorf("Container %s is not running", container.ID) 745 } 746 return container.daemon.Pause(container) 747 } 748 749 func (container *Container) Unpause() error { 750 if !container.IsPaused() { 751 return fmt.Errorf("Container %s is not paused", container.ID) 752 } 753 if !container.IsRunning() { 754 return fmt.Errorf("Container %s is not running", container.ID) 755 } 756 return container.daemon.Unpause(container) 757 } 758 759 func (container *Container) Kill() error { 760 if !container.IsRunning() { 761 return nil 762 } 763 764 // 1. Send SIGKILL 765 if err := container.killPossiblyDeadProcess(9); err != nil { 766 return err 767 } 768 769 // 2. Wait for the process to die, in last resort, try to kill the process directly 770 if _, err := container.WaitStop(10 * time.Second); err != nil { 771 // Ensure that we don't kill ourselves 772 if pid := container.GetPid(); pid != 0 { 773 logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(container.ID)) 774 if err := syscall.Kill(pid, 9); err != nil { 775 if err != syscall.ESRCH { 776 return err 777 } 778 logrus.Debugf("Cannot kill process (pid=%d) with signal 9: no such process.", pid) 779 } 780 } 781 } 782 783 container.WaitStop(-1 * time.Second) 784 return nil 785 } 786 787 func (container *Container) Stop(seconds int) error { 788 if !container.IsRunning() { 789 return nil 790 } 791 792 // 1. Send a SIGTERM 793 if err := container.killPossiblyDeadProcess(15); err != nil { 794 logrus.Infof("Failed to send SIGTERM to the process, force killing") 795 if err := container.killPossiblyDeadProcess(9); err != nil { 796 return err 797 } 798 } 799 800 // 2. Wait for the process to exit on its own 801 if _, err := container.WaitStop(time.Duration(seconds) * time.Second); err != nil { 802 logrus.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds) 803 // 3. If it doesn't, then send SIGKILL 804 if err := container.Kill(); err != nil { 805 container.WaitStop(-1 * time.Second) 806 return err 807 } 808 } 809 return nil 810 } 811 812 func (container *Container) Restart(seconds int) error { 813 // Avoid unnecessarily unmounting and then directly mounting 814 // the container when the container stops and then starts 815 // again 816 if err := container.Mount(); err == nil { 817 defer container.Unmount() 818 } 819 820 if err := container.Stop(seconds); err != nil { 821 return err 822 } 823 return container.Start() 824 } 825 826 func (container *Container) Resize(h, w int) error { 827 if !container.IsRunning() { 828 return fmt.Errorf("Cannot resize container %s, container is not running", container.ID) 829 } 830 return container.command.ProcessConfig.Terminal.Resize(h, w) 831 } 832 833 func (container *Container) ExportRw() (archive.Archive, error) { 834 if err := container.Mount(); err != nil { 835 return nil, err 836 } 837 if container.daemon == nil { 838 return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID) 839 } 840 archive, err := container.daemon.Diff(container) 841 if err != nil { 842 container.Unmount() 843 return nil, err 844 } 845 return ioutils.NewReadCloserWrapper(archive, func() error { 846 err := archive.Close() 847 container.Unmount() 848 return err 849 }), 850 nil 851 } 852 853 func (container *Container) Export() (archive.Archive, error) { 854 if err := container.Mount(); err != nil { 855 return nil, err 856 } 857 858 archive, err := archive.Tar(container.basefs, archive.Uncompressed) 859 if err != nil { 860 container.Unmount() 861 return nil, err 862 } 863 return ioutils.NewReadCloserWrapper(archive, func() error { 864 err := archive.Close() 865 container.Unmount() 866 return err 867 }), 868 nil 869 } 870 871 func (container *Container) Mount() error { 872 return container.daemon.Mount(container) 873 } 874 875 func (container *Container) changes() ([]archive.Change, error) { 876 return container.daemon.Changes(container) 877 } 878 879 func (container *Container) Changes() ([]archive.Change, error) { 880 container.Lock() 881 defer container.Unlock() 882 return container.changes() 883 } 884 885 func (container *Container) GetImage() (*image.Image, error) { 886 if container.daemon == nil { 887 return nil, fmt.Errorf("Can't get image of unregistered container") 888 } 889 return container.daemon.graph.Get(container.ImageID) 890 } 891 892 func (container *Container) Unmount() error { 893 return container.daemon.Unmount(container) 894 } 895 896 func (container *Container) logPath(name string) (string, error) { 897 return container.getRootResourcePath(fmt.Sprintf("%s-%s.log", container.ID, name)) 898 } 899 900 func (container *Container) ReadLog(name string) (io.Reader, error) { 901 pth, err := container.logPath(name) 902 if err != nil { 903 return nil, err 904 } 905 return os.Open(pth) 906 } 907 908 func (container *Container) hostConfigPath() (string, error) { 909 return container.getRootResourcePath("hostconfig.json") 910 } 911 912 func (container *Container) jsonPath() (string, error) { 913 return container.getRootResourcePath("config.json") 914 } 915 916 // This method must be exported to be used from the lxc template 917 // This directory is only usable when the container is running 918 func (container *Container) RootfsPath() string { 919 return container.basefs 920 } 921 922 func validateID(id string) error { 923 if id == "" { 924 return fmt.Errorf("Invalid empty id") 925 } 926 return nil 927 } 928 929 // GetSize, return real size, virtual size 930 func (container *Container) GetSize() (int64, int64) { 931 var ( 932 sizeRw, sizeRootfs int64 933 err error 934 driver = container.daemon.driver 935 ) 936 937 if err := container.Mount(); err != nil { 938 logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) 939 return sizeRw, sizeRootfs 940 } 941 defer container.Unmount() 942 943 initID := fmt.Sprintf("%s-init", container.ID) 944 sizeRw, err = driver.DiffSize(container.ID, initID) 945 if err != nil { 946 logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", driver, container.ID, err) 947 // FIXME: GetSize should return an error. Not changing it now in case 948 // there is a side-effect. 949 sizeRw = -1 950 } 951 952 if _, err = os.Stat(container.basefs); err != nil { 953 if sizeRootfs, err = directory.Size(container.basefs); err != nil { 954 sizeRootfs = -1 955 } 956 } 957 return sizeRw, sizeRootfs 958 } 959 960 func (container *Container) Copy(resource string) (io.ReadCloser, error) { 961 container.Lock() 962 defer container.Unlock() 963 var err error 964 if err := container.Mount(); err != nil { 965 return nil, err 966 } 967 defer func() { 968 if err != nil { 969 container.Unmount() 970 } 971 }() 972 973 if err = container.mountVolumes(); err != nil { 974 container.unmountVolumes() 975 return nil, err 976 } 977 defer func() { 978 if err != nil { 979 container.unmountVolumes() 980 } 981 }() 982 983 basePath, err := container.getResourcePath(resource) 984 if err != nil { 985 return nil, err 986 } 987 988 stat, err := os.Stat(basePath) 989 if err != nil { 990 return nil, err 991 } 992 var filter []string 993 if !stat.IsDir() { 994 d, f := path.Split(basePath) 995 basePath = d 996 filter = []string{f} 997 } else { 998 filter = []string{path.Base(basePath)} 999 basePath = path.Dir(basePath) 1000 } 1001 1002 archive, err := archive.TarWithOptions(basePath, &archive.TarOptions{ 1003 Compression: archive.Uncompressed, 1004 IncludeFiles: filter, 1005 }) 1006 if err != nil { 1007 return nil, err 1008 } 1009 1010 return ioutils.NewReadCloserWrapper(archive, func() error { 1011 err := archive.Close() 1012 container.unmountVolumes() 1013 container.Unmount() 1014 return err 1015 }), 1016 nil 1017 } 1018 1019 // Returns true if the container exposes a certain port 1020 func (container *Container) Exposes(p nat.Port) bool { 1021 _, exists := container.Config.ExposedPorts[p] 1022 return exists 1023 } 1024 1025 func (container *Container) HostConfig() *runconfig.HostConfig { 1026 container.Lock() 1027 res := container.hostConfig 1028 container.Unlock() 1029 return res 1030 } 1031 1032 func (container *Container) SetHostConfig(hostConfig *runconfig.HostConfig) { 1033 container.Lock() 1034 container.hostConfig = hostConfig 1035 container.Unlock() 1036 } 1037 1038 func (container *Container) DisableLink(name string) { 1039 if container.activeLinks != nil { 1040 if link, exists := container.activeLinks[name]; exists { 1041 link.Disable() 1042 } else { 1043 logrus.Debugf("Could not find active link for %s", name) 1044 } 1045 } 1046 } 1047 1048 func (container *Container) setupContainerDns() error { 1049 if container.ResolvConfPath != "" { 1050 // check if this is an existing container that needs DNS update: 1051 if container.UpdateDns { 1052 // read the host's resolv.conf, get the hash and call updateResolvConf 1053 logrus.Debugf("Check container (%s) for update to resolv.conf - UpdateDns flag was set", container.ID) 1054 latestResolvConf, latestHash := resolvconf.GetLastModified() 1055 1056 // clean container resolv.conf re: localhost nameservers and IPv6 NS (if IPv6 disabled) 1057 updatedResolvConf, modified := resolvconf.FilterResolvDns(latestResolvConf, container.daemon.config.Bridge.EnableIPv6) 1058 if modified { 1059 // changes have occurred during resolv.conf localhost cleanup: generate an updated hash 1060 newHash, err := ioutils.HashData(bytes.NewReader(updatedResolvConf)) 1061 if err != nil { 1062 return err 1063 } 1064 latestHash = newHash 1065 } 1066 1067 if err := container.updateResolvConf(updatedResolvConf, latestHash); err != nil { 1068 return err 1069 } 1070 // successful update of the restarting container; set the flag off 1071 container.UpdateDns = false 1072 } 1073 return nil 1074 } 1075 1076 var ( 1077 config = container.hostConfig 1078 daemon = container.daemon 1079 ) 1080 1081 resolvConf, err := resolvconf.Get() 1082 if err != nil { 1083 return err 1084 } 1085 container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf") 1086 if err != nil { 1087 return err 1088 } 1089 1090 if config.NetworkMode != "host" { 1091 // check configurations for any container/daemon dns settings 1092 if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 { 1093 var ( 1094 dns = resolvconf.GetNameservers(resolvConf) 1095 dnsSearch = resolvconf.GetSearchDomains(resolvConf) 1096 ) 1097 if len(config.Dns) > 0 { 1098 dns = config.Dns 1099 } else if len(daemon.config.Dns) > 0 { 1100 dns = daemon.config.Dns 1101 } 1102 if len(config.DnsSearch) > 0 { 1103 dnsSearch = config.DnsSearch 1104 } else if len(daemon.config.DnsSearch) > 0 { 1105 dnsSearch = daemon.config.DnsSearch 1106 } 1107 return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch) 1108 } 1109 1110 // replace any localhost/127.*, and remove IPv6 nameservers if IPv6 disabled in daemon 1111 resolvConf, _ = resolvconf.FilterResolvDns(resolvConf, daemon.config.Bridge.EnableIPv6) 1112 } 1113 //get a sha256 hash of the resolv conf at this point so we can check 1114 //for changes when the host resolv.conf changes (e.g. network update) 1115 resolvHash, err := ioutils.HashData(bytes.NewReader(resolvConf)) 1116 if err != nil { 1117 return err 1118 } 1119 resolvHashFile := container.ResolvConfPath + ".hash" 1120 if err = ioutil.WriteFile(resolvHashFile, []byte(resolvHash), 0644); err != nil { 1121 return err 1122 } 1123 return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644) 1124 } 1125 1126 // called when the host's resolv.conf changes to check whether container's resolv.conf 1127 // is unchanged by the container "user" since container start: if unchanged, the 1128 // container's resolv.conf will be updated to match the host's new resolv.conf 1129 func (container *Container) updateResolvConf(updatedResolvConf []byte, newResolvHash string) error { 1130 1131 if container.ResolvConfPath == "" { 1132 return nil 1133 } 1134 if container.Running { 1135 //set a marker in the hostConfig to update on next start/restart 1136 container.UpdateDns = true 1137 return nil 1138 } 1139 1140 resolvHashFile := container.ResolvConfPath + ".hash" 1141 1142 //read the container's current resolv.conf and compute the hash 1143 resolvBytes, err := ioutil.ReadFile(container.ResolvConfPath) 1144 if err != nil { 1145 return err 1146 } 1147 curHash, err := ioutils.HashData(bytes.NewReader(resolvBytes)) 1148 if err != nil { 1149 return err 1150 } 1151 1152 //read the hash from the last time we wrote resolv.conf in the container 1153 hashBytes, err := ioutil.ReadFile(resolvHashFile) 1154 if err != nil { 1155 if !os.IsNotExist(err) { 1156 return err 1157 } 1158 // backwards compat: if no hash file exists, this container pre-existed from 1159 // a Docker daemon that didn't contain this update feature. Given we can't know 1160 // if the user has modified the resolv.conf since container start time, safer 1161 // to just never update the container's resolv.conf during it's lifetime which 1162 // we can control by setting hashBytes to an empty string 1163 hashBytes = []byte("") 1164 } 1165 1166 //if the user has not modified the resolv.conf of the container since we wrote it last 1167 //we will replace it with the updated resolv.conf from the host 1168 if string(hashBytes) == curHash { 1169 logrus.Debugf("replacing %q with updated host resolv.conf", container.ResolvConfPath) 1170 1171 // for atomic updates to these files, use temporary files with os.Rename: 1172 dir := path.Dir(container.ResolvConfPath) 1173 tmpHashFile, err := ioutil.TempFile(dir, "hash") 1174 if err != nil { 1175 return err 1176 } 1177 tmpResolvFile, err := ioutil.TempFile(dir, "resolv") 1178 if err != nil { 1179 return err 1180 } 1181 1182 // write the updates to the temp files 1183 if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newResolvHash), 0644); err != nil { 1184 return err 1185 } 1186 if err = ioutil.WriteFile(tmpResolvFile.Name(), updatedResolvConf, 0644); err != nil { 1187 return err 1188 } 1189 1190 // rename the temp files for atomic replace 1191 if err = os.Rename(tmpHashFile.Name(), resolvHashFile); err != nil { 1192 return err 1193 } 1194 return os.Rename(tmpResolvFile.Name(), container.ResolvConfPath) 1195 } 1196 return nil 1197 } 1198 1199 func (container *Container) updateParentsHosts() error { 1200 refs := container.daemon.ContainerGraph().RefPaths(container.ID) 1201 for _, ref := range refs { 1202 if ref.ParentID == "0" { 1203 continue 1204 } 1205 1206 c, err := container.daemon.Get(ref.ParentID) 1207 if err != nil { 1208 logrus.Error(err) 1209 } 1210 1211 if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() { 1212 logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress) 1213 if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, ref.Name); err != nil { 1214 logrus.Errorf("Failed to update /etc/hosts in parent container %s for alias %s: %v", c.ID, ref.Name, err) 1215 } 1216 } 1217 } 1218 return nil 1219 } 1220 1221 func (container *Container) initializeNetworking() error { 1222 var err error 1223 if container.hostConfig.NetworkMode.IsHost() { 1224 container.Config.Hostname, err = os.Hostname() 1225 if err != nil { 1226 return err 1227 } 1228 1229 parts := strings.SplitN(container.Config.Hostname, ".", 2) 1230 if len(parts) > 1 { 1231 container.Config.Hostname = parts[0] 1232 container.Config.Domainname = parts[1] 1233 } 1234 1235 content, err := ioutil.ReadFile("/etc/hosts") 1236 if os.IsNotExist(err) { 1237 return container.buildHostnameAndHostsFiles("") 1238 } else if err != nil { 1239 return err 1240 } 1241 1242 if err := container.buildHostnameFile(); err != nil { 1243 return err 1244 } 1245 1246 hostsPath, err := container.getRootResourcePath("hosts") 1247 if err != nil { 1248 return err 1249 } 1250 container.HostsPath = hostsPath 1251 1252 return ioutil.WriteFile(container.HostsPath, content, 0644) 1253 } 1254 if container.hostConfig.NetworkMode.IsContainer() { 1255 // we need to get the hosts files from the container to join 1256 nc, err := container.getNetworkedContainer() 1257 if err != nil { 1258 return err 1259 } 1260 container.HostnamePath = nc.HostnamePath 1261 container.HostsPath = nc.HostsPath 1262 container.ResolvConfPath = nc.ResolvConfPath 1263 container.Config.Hostname = nc.Config.Hostname 1264 container.Config.Domainname = nc.Config.Domainname 1265 return nil 1266 } 1267 if container.daemon.config.DisableNetwork { 1268 container.Config.NetworkDisabled = true 1269 return container.buildHostnameAndHostsFiles("127.0.1.1") 1270 } 1271 if err := container.AllocateNetwork(); err != nil { 1272 return err 1273 } 1274 return container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress) 1275 } 1276 1277 // Make sure the config is compatible with the current kernel 1278 func (container *Container) verifyDaemonSettings() { 1279 if container.hostConfig.Memory > 0 && !container.daemon.sysInfo.MemoryLimit { 1280 logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") 1281 container.hostConfig.Memory = 0 1282 } 1283 if container.hostConfig.Memory > 0 && container.hostConfig.MemorySwap != -1 && !container.daemon.sysInfo.SwapLimit { 1284 logrus.Warnf("Your kernel does not support swap limit capabilities. Limitation discarded.") 1285 container.hostConfig.MemorySwap = -1 1286 } 1287 if container.daemon.sysInfo.IPv4ForwardingDisabled { 1288 logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") 1289 } 1290 } 1291 1292 func (container *Container) setupLinkedContainers() ([]string, error) { 1293 var ( 1294 env []string 1295 daemon = container.daemon 1296 ) 1297 children, err := daemon.Children(container.Name) 1298 if err != nil { 1299 return nil, err 1300 } 1301 1302 if len(children) > 0 { 1303 container.activeLinks = make(map[string]*links.Link, len(children)) 1304 1305 // If we encounter an error make sure that we rollback any network 1306 // config and iptables changes 1307 rollback := func() { 1308 for _, link := range container.activeLinks { 1309 link.Disable() 1310 } 1311 container.activeLinks = nil 1312 } 1313 1314 for linkAlias, child := range children { 1315 if !child.IsRunning() { 1316 return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias) 1317 } 1318 1319 link, err := links.NewLink( 1320 container.NetworkSettings.IPAddress, 1321 child.NetworkSettings.IPAddress, 1322 linkAlias, 1323 child.Config.Env, 1324 child.Config.ExposedPorts, 1325 ) 1326 1327 if err != nil { 1328 rollback() 1329 return nil, err 1330 } 1331 1332 container.activeLinks[link.Alias()] = link 1333 if err := link.Enable(); err != nil { 1334 rollback() 1335 return nil, err 1336 } 1337 1338 for _, envVar := range link.ToEnv() { 1339 env = append(env, envVar) 1340 } 1341 } 1342 } 1343 return env, nil 1344 } 1345 1346 func (container *Container) createDaemonEnvironment(linkedEnv []string) []string { 1347 // if a domain name was specified, append it to the hostname (see #7851) 1348 fullHostname := container.Config.Hostname 1349 if container.Config.Domainname != "" { 1350 fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) 1351 } 1352 // Setup environment 1353 env := []string{ 1354 "PATH=" + DefaultPathEnv, 1355 "HOSTNAME=" + fullHostname, 1356 // Note: we don't set HOME here because it'll get autoset intelligently 1357 // based on the value of USER inside dockerinit, but only if it isn't 1358 // set already (ie, that can be overridden by setting HOME via -e or ENV 1359 // in a Dockerfile). 1360 } 1361 if container.Config.Tty { 1362 env = append(env, "TERM=xterm") 1363 } 1364 env = append(env, linkedEnv...) 1365 // because the env on the container can override certain default values 1366 // we need to replace the 'env' keys where they match and append anything 1367 // else. 1368 env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) 1369 1370 return env 1371 } 1372 1373 func (container *Container) setupWorkingDirectory() error { 1374 if container.Config.WorkingDir != "" { 1375 container.Config.WorkingDir = path.Clean(container.Config.WorkingDir) 1376 1377 pth, err := container.getResourcePath(container.Config.WorkingDir) 1378 if err != nil { 1379 return err 1380 } 1381 1382 pthInfo, err := os.Stat(pth) 1383 if err != nil { 1384 if !os.IsNotExist(err) { 1385 return err 1386 } 1387 1388 if err := os.MkdirAll(pth, 0755); err != nil { 1389 return err 1390 } 1391 } 1392 if pthInfo != nil && !pthInfo.IsDir() { 1393 return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir) 1394 } 1395 } 1396 return nil 1397 } 1398 1399 func (container *Container) startLogging() error { 1400 cfg := container.hostConfig.LogConfig 1401 if cfg.Type == "" { 1402 cfg = container.daemon.defaultLogConfig 1403 } 1404 var l logger.Logger 1405 switch cfg.Type { 1406 case "json-file": 1407 pth, err := container.logPath("json") 1408 if err != nil { 1409 return err 1410 } 1411 container.LogPath = pth 1412 1413 dl, err := jsonfilelog.New(pth) 1414 if err != nil { 1415 return err 1416 } 1417 l = dl 1418 case "syslog": 1419 dl, err := syslog.New(container.ID[:12]) 1420 if err != nil { 1421 return err 1422 } 1423 l = dl 1424 case "journald": 1425 dl, err := journald.New(container.ID[:12]) 1426 if err != nil { 1427 return err 1428 } 1429 l = dl 1430 case "none": 1431 return nil 1432 default: 1433 return fmt.Errorf("Unknown logging driver: %s", cfg.Type) 1434 } 1435 1436 copier, err := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) 1437 if err != nil { 1438 return err 1439 } 1440 container.logCopier = copier 1441 copier.Run() 1442 container.logDriver = l 1443 1444 return nil 1445 } 1446 1447 func (container *Container) waitForStart() error { 1448 container.monitor = newContainerMonitor(container, container.hostConfig.RestartPolicy) 1449 1450 // block until we either receive an error from the initial start of the container's 1451 // process or until the process is running in the container 1452 select { 1453 case <-container.monitor.startSignal: 1454 case err := <-promise.Go(container.monitor.Start): 1455 return err 1456 } 1457 1458 return nil 1459 } 1460 1461 func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bindings nat.PortMap) error { 1462 binding := bindings[port] 1463 if container.hostConfig.PublishAllPorts && len(binding) == 0 { 1464 binding = append(binding, nat.PortBinding{}) 1465 } 1466 1467 for i := 0; i < len(binding); i++ { 1468 b, err := bridge.AllocatePort(container.ID, port, binding[i]) 1469 if err != nil { 1470 return err 1471 } 1472 binding[i] = b 1473 } 1474 bindings[port] = binding 1475 return nil 1476 } 1477 1478 func (container *Container) GetProcessLabel() string { 1479 // even if we have a process label return "" if we are running 1480 // in privileged mode 1481 if container.hostConfig.Privileged { 1482 return "" 1483 } 1484 return container.ProcessLabel 1485 } 1486 1487 func (container *Container) GetMountLabel() string { 1488 if container.hostConfig.Privileged { 1489 return "" 1490 } 1491 return container.MountLabel 1492 } 1493 1494 func (container *Container) getIpcContainer() (*Container, error) { 1495 containerID := container.hostConfig.IpcMode.Container() 1496 c, err := container.daemon.Get(containerID) 1497 if err != nil { 1498 return nil, err 1499 } 1500 if !c.IsRunning() { 1501 return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID) 1502 } 1503 return c, nil 1504 } 1505 1506 func (container *Container) getNetworkedContainer() (*Container, error) { 1507 parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2) 1508 switch parts[0] { 1509 case "container": 1510 if len(parts) != 2 { 1511 return nil, fmt.Errorf("no container specified to join network") 1512 } 1513 nc, err := container.daemon.Get(parts[1]) 1514 if err != nil { 1515 return nil, err 1516 } 1517 if container == nc { 1518 return nil, fmt.Errorf("cannot join own network") 1519 } 1520 if !nc.IsRunning() { 1521 return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1]) 1522 } 1523 return nc, nil 1524 default: 1525 return nil, fmt.Errorf("network mode not set to container") 1526 } 1527 } 1528 1529 func (container *Container) Stats() (*execdriver.ResourceStats, error) { 1530 return container.daemon.Stats(container) 1531 } 1532 1533 func (c *Container) LogDriverType() string { 1534 c.Lock() 1535 defer c.Unlock() 1536 if c.hostConfig.LogConfig.Type == "" { 1537 return c.daemon.defaultLogConfig.Type 1538 } 1539 return c.hostConfig.LogConfig.Type 1540 }