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