github.com/sirupsen/docker@v0.10.1-0.20150325003727-22dba32b4dab/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/directory" 34 "github.com/docker/docker/pkg/etchosts" 35 "github.com/docker/docker/pkg/ioutils" 36 "github.com/docker/docker/pkg/promise" 37 "github.com/docker/docker/pkg/resolvconf" 38 "github.com/docker/docker/pkg/stringid" 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 we encounter an error during start we need to ensure that any other 364 // setup has been cleaned up properly 365 defer func() { 366 if err != nil { 367 container.setError(err) 368 // if no one else has set it, make sure we don't leave it at zero 369 if container.ExitCode == 0 { 370 container.ExitCode = 128 371 } 372 container.toDisk() 373 container.cleanup() 374 } 375 }() 376 377 if err := container.setupContainerDns(); err != nil { 378 return err 379 } 380 if err := container.Mount(); err != nil { 381 return err 382 } 383 if err := container.initializeNetworking(); err != nil { 384 return err 385 } 386 if err := container.updateParentsHosts(); err != nil { 387 return err 388 } 389 container.verifyDaemonSettings() 390 if err := container.prepareVolumes(); err != nil { 391 return err 392 } 393 linkedEnv, err := container.setupLinkedContainers() 394 if err != nil { 395 return err 396 } 397 if err := container.setupWorkingDirectory(); err != nil { 398 return err 399 } 400 env := container.createDaemonEnvironment(linkedEnv) 401 if err := populateCommand(container, env); err != nil { 402 return err 403 } 404 if err := container.setupMounts(); err != nil { 405 return err 406 } 407 408 return container.waitForStart() 409 } 410 411 func (container *Container) Run() error { 412 if err := container.Start(); err != nil { 413 return err 414 } 415 container.WaitStop(-1 * time.Second) 416 return nil 417 } 418 419 func (container *Container) Output() (output []byte, err error) { 420 pipe := container.StdoutPipe() 421 defer pipe.Close() 422 if err := container.Start(); err != nil { 423 return nil, err 424 } 425 output, err = ioutil.ReadAll(pipe) 426 container.WaitStop(-1 * time.Second) 427 return output, err 428 } 429 430 // StreamConfig.StdinPipe returns a WriteCloser which can be used to feed data 431 // to the standard input of the container's active process. 432 // Container.StdoutPipe and Container.StderrPipe each return a ReadCloser 433 // which can be used to retrieve the standard output (and error) generated 434 // by the container's active process. The output (and error) are actually 435 // copied and delivered to all StdoutPipe and StderrPipe consumers, using 436 // a kind of "broadcaster". 437 438 func (streamConfig *StreamConfig) StdinPipe() io.WriteCloser { 439 return streamConfig.stdinPipe 440 } 441 442 func (streamConfig *StreamConfig) StdoutPipe() io.ReadCloser { 443 reader, writer := io.Pipe() 444 streamConfig.stdout.AddWriter(writer, "") 445 return ioutils.NewBufReader(reader) 446 } 447 448 func (streamConfig *StreamConfig) StderrPipe() io.ReadCloser { 449 reader, writer := io.Pipe() 450 streamConfig.stderr.AddWriter(writer, "") 451 return ioutils.NewBufReader(reader) 452 } 453 454 func (streamConfig *StreamConfig) StdoutLogPipe() io.ReadCloser { 455 reader, writer := io.Pipe() 456 streamConfig.stdout.AddWriter(writer, "stdout") 457 return ioutils.NewBufReader(reader) 458 } 459 460 func (streamConfig *StreamConfig) StderrLogPipe() io.ReadCloser { 461 reader, writer := io.Pipe() 462 streamConfig.stderr.AddWriter(writer, "stderr") 463 return ioutils.NewBufReader(reader) 464 } 465 466 func (container *Container) buildHostnameFile() error { 467 hostnamePath, err := container.getRootResourcePath("hostname") 468 if err != nil { 469 return err 470 } 471 container.HostnamePath = hostnamePath 472 473 if container.Config.Domainname != "" { 474 return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) 475 } 476 return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) 477 } 478 479 func (container *Container) buildHostsFiles(IP string) error { 480 481 hostsPath, err := container.getRootResourcePath("hosts") 482 if err != nil { 483 return err 484 } 485 container.HostsPath = hostsPath 486 487 var extraContent []etchosts.Record 488 489 children, err := container.daemon.Children(container.Name) 490 if err != nil { 491 return err 492 } 493 494 for linkAlias, child := range children { 495 _, alias := path.Split(linkAlias) 496 // allow access to the linked container via the alias, real name, and container hostname 497 aliasList := alias + " " + child.Config.Hostname 498 // only add the name if alias isn't equal to the name 499 if alias != child.Name[1:] { 500 aliasList = aliasList + " " + child.Name[1:] 501 } 502 extraContent = append(extraContent, etchosts.Record{Hosts: aliasList, IP: child.NetworkSettings.IPAddress}) 503 } 504 505 for _, extraHost := range container.hostConfig.ExtraHosts { 506 // allow IPv6 addresses in extra hosts; only split on first ":" 507 parts := strings.SplitN(extraHost, ":", 2) 508 extraContent = append(extraContent, etchosts.Record{Hosts: parts[0], IP: parts[1]}) 509 } 510 511 return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, extraContent) 512 } 513 514 func (container *Container) buildHostnameAndHostsFiles(IP string) error { 515 if err := container.buildHostnameFile(); err != nil { 516 return err 517 } 518 519 return container.buildHostsFiles(IP) 520 } 521 522 func (container *Container) AllocateNetwork() error { 523 mode := container.hostConfig.NetworkMode 524 if container.Config.NetworkDisabled || !mode.IsPrivate() { 525 return nil 526 } 527 528 var ( 529 env *engine.Env 530 err error 531 eng = container.daemon.eng 532 ) 533 534 job := eng.Job("allocate_interface", container.ID) 535 job.Setenv("RequestedMac", container.Config.MacAddress) 536 if env, err = job.Stdout.AddEnv(); err != nil { 537 return err 538 } 539 if err = job.Run(); err != nil { 540 return err 541 } 542 543 // Error handling: At this point, the interface is allocated so we have to 544 // make sure that it is always released in case of error, otherwise we 545 // might leak resources. 546 547 if container.Config.PortSpecs != nil { 548 if err = migratePortMappings(container.Config, container.hostConfig); err != nil { 549 eng.Job("release_interface", container.ID).Run() 550 return err 551 } 552 container.Config.PortSpecs = nil 553 if err = container.WriteHostConfig(); err != nil { 554 eng.Job("release_interface", container.ID).Run() 555 return err 556 } 557 } 558 559 var ( 560 portSpecs = make(nat.PortSet) 561 bindings = make(nat.PortMap) 562 ) 563 564 if container.Config.ExposedPorts != nil { 565 portSpecs = container.Config.ExposedPorts 566 } 567 568 if container.hostConfig.PortBindings != nil { 569 for p, b := range container.hostConfig.PortBindings { 570 bindings[p] = []nat.PortBinding{} 571 for _, bb := range b { 572 bindings[p] = append(bindings[p], nat.PortBinding{ 573 HostIp: bb.HostIp, 574 HostPort: bb.HostPort, 575 }) 576 } 577 } 578 } 579 580 container.NetworkSettings.PortMapping = nil 581 582 for port := range portSpecs { 583 if err = container.allocatePort(eng, port, bindings); err != nil { 584 eng.Job("release_interface", container.ID).Run() 585 return err 586 } 587 } 588 container.WriteHostConfig() 589 590 container.NetworkSettings.Ports = bindings 591 container.NetworkSettings.Bridge = env.Get("Bridge") 592 container.NetworkSettings.IPAddress = env.Get("IP") 593 container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen") 594 container.NetworkSettings.MacAddress = env.Get("MacAddress") 595 container.NetworkSettings.Gateway = env.Get("Gateway") 596 container.NetworkSettings.LinkLocalIPv6Address = env.Get("LinkLocalIPv6") 597 container.NetworkSettings.LinkLocalIPv6PrefixLen = 64 598 container.NetworkSettings.GlobalIPv6Address = env.Get("GlobalIPv6") 599 container.NetworkSettings.GlobalIPv6PrefixLen = env.GetInt("GlobalIPv6PrefixLen") 600 container.NetworkSettings.IPv6Gateway = env.Get("IPv6Gateway") 601 602 return nil 603 } 604 605 func (container *Container) ReleaseNetwork() { 606 if container.Config.NetworkDisabled || !container.hostConfig.NetworkMode.IsPrivate() { 607 return 608 } 609 eng := container.daemon.eng 610 611 job := eng.Job("release_interface", container.ID) 612 job.SetenvBool("overrideShutdown", true) 613 job.Run() 614 container.NetworkSettings = &NetworkSettings{} 615 } 616 617 func (container *Container) isNetworkAllocated() bool { 618 return container.NetworkSettings.IPAddress != "" 619 } 620 621 func (container *Container) RestoreNetwork() error { 622 mode := container.hostConfig.NetworkMode 623 // Don't attempt a restore if we previously didn't allocate networking. 624 // This might be a legacy container with no network allocated, in which case the 625 // allocation will happen once and for all at start. 626 if !container.isNetworkAllocated() || container.Config.NetworkDisabled || !mode.IsPrivate() { 627 return nil 628 } 629 630 eng := container.daemon.eng 631 632 // Re-allocate the interface with the same IP and MAC address. 633 job := eng.Job("allocate_interface", container.ID) 634 job.Setenv("RequestedIP", container.NetworkSettings.IPAddress) 635 job.Setenv("RequestedMac", container.NetworkSettings.MacAddress) 636 if err := job.Run(); err != nil { 637 return err 638 } 639 640 // Re-allocate any previously allocated ports. 641 for port := range container.NetworkSettings.Ports { 642 if err := container.allocatePort(eng, port, container.NetworkSettings.Ports); err != nil { 643 return err 644 } 645 } 646 return nil 647 } 648 649 // cleanup releases any network resources allocated to the container along with any rules 650 // around how containers are linked together. It also unmounts the container's root filesystem. 651 func (container *Container) cleanup() { 652 container.ReleaseNetwork() 653 654 // Disable all active links 655 if container.activeLinks != nil { 656 for _, link := range container.activeLinks { 657 link.Disable() 658 } 659 } 660 661 if err := container.Unmount(); err != nil { 662 log.Errorf("%v: Failed to umount filesystem: %v", container.ID, err) 663 } 664 665 for _, eConfig := range container.execCommands.s { 666 container.daemon.unregisterExecCommand(eConfig) 667 } 668 } 669 670 func (container *Container) KillSig(sig int) error { 671 log.Debugf("Sending %d to %s", sig, container.ID) 672 container.Lock() 673 defer container.Unlock() 674 675 // We could unpause the container for them rather than returning this error 676 if container.Paused { 677 return fmt.Errorf("Container %s is paused. Unpause the container before stopping", container.ID) 678 } 679 680 if !container.Running { 681 return nil 682 } 683 684 // signal to the monitor that it should not restart the container 685 // after we send the kill signal 686 container.monitor.ExitOnNext() 687 688 // if the container is currently restarting we do not need to send the signal 689 // to the process. Telling the monitor that it should exit on it's next event 690 // loop is enough 691 if container.Restarting { 692 return nil 693 } 694 695 return container.daemon.Kill(container, sig) 696 } 697 698 // Wrapper aroung KillSig() suppressing "no such process" error. 699 func (container *Container) killPossiblyDeadProcess(sig int) error { 700 err := container.KillSig(sig) 701 if err == syscall.ESRCH { 702 log.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.GetPid(), sig) 703 return nil 704 } 705 return err 706 } 707 708 func (container *Container) Pause() error { 709 if container.IsPaused() { 710 return fmt.Errorf("Container %s is already paused", container.ID) 711 } 712 if !container.IsRunning() { 713 return fmt.Errorf("Container %s is not running", container.ID) 714 } 715 return container.daemon.Pause(container) 716 } 717 718 func (container *Container) Unpause() error { 719 if !container.IsPaused() { 720 return fmt.Errorf("Container %s is not paused", container.ID) 721 } 722 if !container.IsRunning() { 723 return fmt.Errorf("Container %s is not running", container.ID) 724 } 725 return container.daemon.Unpause(container) 726 } 727 728 func (container *Container) Kill() error { 729 if !container.IsRunning() { 730 return nil 731 } 732 733 // 1. Send SIGKILL 734 if err := container.killPossiblyDeadProcess(9); err != nil { 735 return err 736 } 737 738 // 2. Wait for the process to die, in last resort, try to kill the process directly 739 if _, err := container.WaitStop(10 * time.Second); err != nil { 740 // Ensure that we don't kill ourselves 741 if pid := container.GetPid(); pid != 0 { 742 log.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(container.ID)) 743 if err := syscall.Kill(pid, 9); err != nil { 744 if err != syscall.ESRCH { 745 return err 746 } 747 log.Debugf("Cannot kill process (pid=%d) with signal 9: no such process.", pid) 748 } 749 } 750 } 751 752 container.WaitStop(-1 * time.Second) 753 return nil 754 } 755 756 func (container *Container) Stop(seconds int) error { 757 if !container.IsRunning() { 758 return nil 759 } 760 761 // 1. Send a SIGTERM 762 if err := container.killPossiblyDeadProcess(15); err != nil { 763 log.Infof("Failed to send SIGTERM to the process, force killing") 764 if err := container.killPossiblyDeadProcess(9); err != nil { 765 return err 766 } 767 } 768 769 // 2. Wait for the process to exit on its own 770 if _, err := container.WaitStop(time.Duration(seconds) * time.Second); err != nil { 771 log.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds) 772 // 3. If it doesn't, then send SIGKILL 773 if err := container.Kill(); err != nil { 774 container.WaitStop(-1 * time.Second) 775 return err 776 } 777 } 778 return nil 779 } 780 781 func (container *Container) Restart(seconds int) error { 782 // Avoid unnecessarily unmounting and then directly mounting 783 // the container when the container stops and then starts 784 // again 785 if err := container.Mount(); err == nil { 786 defer container.Unmount() 787 } 788 789 if err := container.Stop(seconds); err != nil { 790 return err 791 } 792 return container.Start() 793 } 794 795 func (container *Container) Resize(h, w int) error { 796 if !container.IsRunning() { 797 return fmt.Errorf("Cannot resize container %s, container is not running", container.ID) 798 } 799 return container.command.ProcessConfig.Terminal.Resize(h, w) 800 } 801 802 func (container *Container) ExportRw() (archive.Archive, error) { 803 if err := container.Mount(); err != nil { 804 return nil, err 805 } 806 if container.daemon == nil { 807 return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID) 808 } 809 archive, err := container.daemon.Diff(container) 810 if err != nil { 811 container.Unmount() 812 return nil, err 813 } 814 return ioutils.NewReadCloserWrapper(archive, func() error { 815 err := archive.Close() 816 container.Unmount() 817 return err 818 }), 819 nil 820 } 821 822 func (container *Container) Export() (archive.Archive, error) { 823 if err := container.Mount(); err != nil { 824 return nil, err 825 } 826 827 archive, err := archive.Tar(container.basefs, archive.Uncompressed) 828 if err != nil { 829 container.Unmount() 830 return nil, err 831 } 832 return ioutils.NewReadCloserWrapper(archive, func() error { 833 err := archive.Close() 834 container.Unmount() 835 return err 836 }), 837 nil 838 } 839 840 func (container *Container) Mount() error { 841 return container.daemon.Mount(container) 842 } 843 844 func (container *Container) changes() ([]archive.Change, error) { 845 return container.daemon.Changes(container) 846 } 847 848 func (container *Container) Changes() ([]archive.Change, error) { 849 container.Lock() 850 defer container.Unlock() 851 return container.changes() 852 } 853 854 func (container *Container) GetImage() (*image.Image, error) { 855 if container.daemon == nil { 856 return nil, fmt.Errorf("Can't get image of unregistered container") 857 } 858 return container.daemon.graph.Get(container.ImageID) 859 } 860 861 func (container *Container) Unmount() error { 862 return container.daemon.Unmount(container) 863 } 864 865 func (container *Container) logPath(name string) (string, error) { 866 return container.getRootResourcePath(fmt.Sprintf("%s-%s.log", container.ID, name)) 867 } 868 869 func (container *Container) ReadLog(name string) (io.Reader, error) { 870 pth, err := container.logPath(name) 871 if err != nil { 872 return nil, err 873 } 874 return os.Open(pth) 875 } 876 877 func (container *Container) hostConfigPath() (string, error) { 878 return container.getRootResourcePath("hostconfig.json") 879 } 880 881 func (container *Container) jsonPath() (string, error) { 882 return container.getRootResourcePath("config.json") 883 } 884 885 // This method must be exported to be used from the lxc template 886 // This directory is only usable when the container is running 887 func (container *Container) RootfsPath() string { 888 return container.basefs 889 } 890 891 func validateID(id string) error { 892 if id == "" { 893 return fmt.Errorf("Invalid empty id") 894 } 895 return nil 896 } 897 898 // GetSize, return real size, virtual size 899 func (container *Container) GetSize() (int64, int64) { 900 var ( 901 sizeRw, sizeRootfs int64 902 err error 903 driver = container.daemon.driver 904 ) 905 906 if err := container.Mount(); err != nil { 907 log.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) 908 return sizeRw, sizeRootfs 909 } 910 defer container.Unmount() 911 912 initID := fmt.Sprintf("%s-init", container.ID) 913 sizeRw, err = driver.DiffSize(container.ID, initID) 914 if err != nil { 915 log.Errorf("Driver %s couldn't return diff size of container %s: %s", driver, container.ID, err) 916 // FIXME: GetSize should return an error. Not changing it now in case 917 // there is a side-effect. 918 sizeRw = -1 919 } 920 921 if _, err = os.Stat(container.basefs); err != nil { 922 if sizeRootfs, err = directory.Size(container.basefs); err != nil { 923 sizeRootfs = -1 924 } 925 } 926 return sizeRw, sizeRootfs 927 } 928 929 func (container *Container) Copy(resource string) (io.ReadCloser, error) { 930 if err := container.Mount(); err != nil { 931 return nil, err 932 } 933 934 basePath, err := container.getResourcePath(resource) 935 if err != nil { 936 container.Unmount() 937 return nil, err 938 } 939 940 // Check if this is actually in a volume 941 for _, mnt := range container.VolumeMounts() { 942 if len(mnt.MountToPath) > 0 && strings.HasPrefix(resource, mnt.MountToPath[1:]) { 943 return mnt.Export(resource) 944 } 945 } 946 947 stat, err := os.Stat(basePath) 948 if err != nil { 949 container.Unmount() 950 return nil, err 951 } 952 var filter []string 953 if !stat.IsDir() { 954 d, f := path.Split(basePath) 955 basePath = d 956 filter = []string{f} 957 } else { 958 filter = []string{path.Base(basePath)} 959 basePath = path.Dir(basePath) 960 } 961 962 archive, err := archive.TarWithOptions(basePath, &archive.TarOptions{ 963 Compression: archive.Uncompressed, 964 IncludeFiles: filter, 965 }) 966 if err != nil { 967 container.Unmount() 968 return nil, err 969 } 970 return ioutils.NewReadCloserWrapper(archive, func() error { 971 err := archive.Close() 972 container.Unmount() 973 return err 974 }), 975 nil 976 } 977 978 // Returns true if the container exposes a certain port 979 func (container *Container) Exposes(p nat.Port) bool { 980 _, exists := container.Config.ExposedPorts[p] 981 return exists 982 } 983 984 func (container *Container) GetPtyMaster() (libcontainer.Console, error) { 985 ttyConsole, ok := container.command.ProcessConfig.Terminal.(execdriver.TtyTerminal) 986 if !ok { 987 return nil, ErrNoTTY 988 } 989 return ttyConsole.Master(), nil 990 } 991 992 func (container *Container) HostConfig() *runconfig.HostConfig { 993 container.Lock() 994 res := container.hostConfig 995 container.Unlock() 996 return res 997 } 998 999 func (container *Container) SetHostConfig(hostConfig *runconfig.HostConfig) { 1000 container.Lock() 1001 container.hostConfig = hostConfig 1002 container.Unlock() 1003 } 1004 1005 func (container *Container) DisableLink(name string) { 1006 if container.activeLinks != nil { 1007 if link, exists := container.activeLinks[name]; exists { 1008 link.Disable() 1009 } else { 1010 log.Debugf("Could not find active link for %s", name) 1011 } 1012 } 1013 } 1014 1015 func (container *Container) setupContainerDns() error { 1016 if container.ResolvConfPath != "" { 1017 // check if this is an existing container that needs DNS update: 1018 if container.UpdateDns { 1019 // read the host's resolv.conf, get the hash and call updateResolvConf 1020 log.Debugf("Check container (%s) for update to resolv.conf - UpdateDns flag was set", container.ID) 1021 latestResolvConf, latestHash := resolvconf.GetLastModified() 1022 1023 // clean container resolv.conf re: localhost nameservers and IPv6 NS (if IPv6 disabled) 1024 updatedResolvConf, modified := resolvconf.FilterResolvDns(latestResolvConf, container.daemon.config.EnableIPv6) 1025 if modified { 1026 // changes have occurred during resolv.conf localhost cleanup: generate an updated hash 1027 newHash, err := utils.HashData(bytes.NewReader(updatedResolvConf)) 1028 if err != nil { 1029 return err 1030 } 1031 latestHash = newHash 1032 } 1033 1034 if err := container.updateResolvConf(updatedResolvConf, latestHash); err != nil { 1035 return err 1036 } 1037 // successful update of the restarting container; set the flag off 1038 container.UpdateDns = false 1039 } 1040 return nil 1041 } 1042 1043 var ( 1044 config = container.hostConfig 1045 daemon = container.daemon 1046 ) 1047 1048 resolvConf, err := resolvconf.Get() 1049 if err != nil { 1050 return err 1051 } 1052 container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf") 1053 if err != nil { 1054 return err 1055 } 1056 1057 if config.NetworkMode != "host" { 1058 // check configurations for any container/daemon dns settings 1059 if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 { 1060 var ( 1061 dns = resolvconf.GetNameservers(resolvConf) 1062 dnsSearch = resolvconf.GetSearchDomains(resolvConf) 1063 ) 1064 if len(config.Dns) > 0 { 1065 dns = config.Dns 1066 } else if len(daemon.config.Dns) > 0 { 1067 dns = daemon.config.Dns 1068 } 1069 if len(config.DnsSearch) > 0 { 1070 dnsSearch = config.DnsSearch 1071 } else if len(daemon.config.DnsSearch) > 0 { 1072 dnsSearch = daemon.config.DnsSearch 1073 } 1074 return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch) 1075 } 1076 1077 // replace any localhost/127.*, and remove IPv6 nameservers if IPv6 disabled in daemon 1078 resolvConf, _ = resolvconf.FilterResolvDns(resolvConf, daemon.config.EnableIPv6) 1079 } 1080 //get a sha256 hash of the resolv conf at this point so we can check 1081 //for changes when the host resolv.conf changes (e.g. network update) 1082 resolvHash, err := utils.HashData(bytes.NewReader(resolvConf)) 1083 if err != nil { 1084 return err 1085 } 1086 resolvHashFile := container.ResolvConfPath + ".hash" 1087 if err = ioutil.WriteFile(resolvHashFile, []byte(resolvHash), 0644); err != nil { 1088 return err 1089 } 1090 return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644) 1091 } 1092 1093 // called when the host's resolv.conf changes to check whether container's resolv.conf 1094 // is unchanged by the container "user" since container start: if unchanged, the 1095 // container's resolv.conf will be updated to match the host's new resolv.conf 1096 func (container *Container) updateResolvConf(updatedResolvConf []byte, newResolvHash string) error { 1097 1098 if container.ResolvConfPath == "" { 1099 return nil 1100 } 1101 if container.Running { 1102 //set a marker in the hostConfig to update on next start/restart 1103 container.UpdateDns = true 1104 return nil 1105 } 1106 1107 resolvHashFile := container.ResolvConfPath + ".hash" 1108 1109 //read the container's current resolv.conf and compute the hash 1110 resolvBytes, err := ioutil.ReadFile(container.ResolvConfPath) 1111 if err != nil { 1112 return err 1113 } 1114 curHash, err := utils.HashData(bytes.NewReader(resolvBytes)) 1115 if err != nil { 1116 return err 1117 } 1118 1119 //read the hash from the last time we wrote resolv.conf in the container 1120 hashBytes, err := ioutil.ReadFile(resolvHashFile) 1121 if err != nil { 1122 if !os.IsNotExist(err) { 1123 return err 1124 } 1125 // backwards compat: if no hash file exists, this container pre-existed from 1126 // a Docker daemon that didn't contain this update feature. Given we can't know 1127 // if the user has modified the resolv.conf since container start time, safer 1128 // to just never update the container's resolv.conf during it's lifetime which 1129 // we can control by setting hashBytes to an empty string 1130 hashBytes = []byte("") 1131 } 1132 1133 //if the user has not modified the resolv.conf of the container since we wrote it last 1134 //we will replace it with the updated resolv.conf from the host 1135 if string(hashBytes) == curHash { 1136 log.Debugf("replacing %q with updated host resolv.conf", container.ResolvConfPath) 1137 1138 // for atomic updates to these files, use temporary files with os.Rename: 1139 dir := path.Dir(container.ResolvConfPath) 1140 tmpHashFile, err := ioutil.TempFile(dir, "hash") 1141 if err != nil { 1142 return err 1143 } 1144 tmpResolvFile, err := ioutil.TempFile(dir, "resolv") 1145 if err != nil { 1146 return err 1147 } 1148 1149 // write the updates to the temp files 1150 if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newResolvHash), 0644); err != nil { 1151 return err 1152 } 1153 if err = ioutil.WriteFile(tmpResolvFile.Name(), updatedResolvConf, 0644); err != nil { 1154 return err 1155 } 1156 1157 // rename the temp files for atomic replace 1158 if err = os.Rename(tmpHashFile.Name(), resolvHashFile); err != nil { 1159 return err 1160 } 1161 return os.Rename(tmpResolvFile.Name(), container.ResolvConfPath) 1162 } 1163 return nil 1164 } 1165 1166 func (container *Container) updateParentsHosts() error { 1167 refs := container.daemon.ContainerGraph().RefPaths(container.ID) 1168 for _, ref := range refs { 1169 if ref.ParentID == "0" { 1170 continue 1171 } 1172 1173 c, err := container.daemon.Get(ref.ParentID) 1174 if err != nil { 1175 log.Error(err) 1176 } 1177 1178 if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() { 1179 log.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress) 1180 if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, ref.Name); err != nil { 1181 log.Errorf("Failed to update /etc/hosts in parent container %s for alias %s: %v", c.ID, ref.Name, err) 1182 } 1183 } 1184 } 1185 return nil 1186 } 1187 1188 func (container *Container) initializeNetworking() error { 1189 var err error 1190 if container.hostConfig.NetworkMode.IsHost() { 1191 container.Config.Hostname, err = os.Hostname() 1192 if err != nil { 1193 return err 1194 } 1195 1196 parts := strings.SplitN(container.Config.Hostname, ".", 2) 1197 if len(parts) > 1 { 1198 container.Config.Hostname = parts[0] 1199 container.Config.Domainname = parts[1] 1200 } 1201 1202 content, err := ioutil.ReadFile("/etc/hosts") 1203 if os.IsNotExist(err) { 1204 return container.buildHostnameAndHostsFiles("") 1205 } else if err != nil { 1206 return err 1207 } 1208 1209 if err := container.buildHostnameFile(); err != nil { 1210 return err 1211 } 1212 1213 hostsPath, err := container.getRootResourcePath("hosts") 1214 if err != nil { 1215 return err 1216 } 1217 container.HostsPath = hostsPath 1218 1219 return ioutil.WriteFile(container.HostsPath, content, 0644) 1220 } 1221 if container.hostConfig.NetworkMode.IsContainer() { 1222 // we need to get the hosts files from the container to join 1223 nc, err := container.getNetworkedContainer() 1224 if err != nil { 1225 return err 1226 } 1227 container.HostnamePath = nc.HostnamePath 1228 container.HostsPath = nc.HostsPath 1229 container.ResolvConfPath = nc.ResolvConfPath 1230 container.Config.Hostname = nc.Config.Hostname 1231 container.Config.Domainname = nc.Config.Domainname 1232 return nil 1233 } 1234 if container.daemon.config.DisableNetwork { 1235 container.Config.NetworkDisabled = true 1236 return container.buildHostnameAndHostsFiles("127.0.1.1") 1237 } 1238 if err := container.AllocateNetwork(); err != nil { 1239 return err 1240 } 1241 return container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress) 1242 } 1243 1244 // Make sure the config is compatible with the current kernel 1245 func (container *Container) verifyDaemonSettings() { 1246 if container.Config.Memory > 0 && !container.daemon.sysInfo.MemoryLimit { 1247 log.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") 1248 container.Config.Memory = 0 1249 } 1250 if container.Config.Memory > 0 && !container.daemon.sysInfo.SwapLimit { 1251 log.Warnf("Your kernel does not support swap limit capabilities. Limitation discarded.") 1252 container.Config.MemorySwap = -1 1253 } 1254 if container.daemon.sysInfo.IPv4ForwardingDisabled { 1255 log.Warnf("IPv4 forwarding is disabled. Networking will not work") 1256 } 1257 } 1258 1259 func (container *Container) setupLinkedContainers() ([]string, error) { 1260 var ( 1261 env []string 1262 daemon = container.daemon 1263 ) 1264 children, err := daemon.Children(container.Name) 1265 if err != nil { 1266 return nil, err 1267 } 1268 1269 if len(children) > 0 { 1270 container.activeLinks = make(map[string]*links.Link, len(children)) 1271 1272 // If we encounter an error make sure that we rollback any network 1273 // config and iptables changes 1274 rollback := func() { 1275 for _, link := range container.activeLinks { 1276 link.Disable() 1277 } 1278 container.activeLinks = nil 1279 } 1280 1281 for linkAlias, child := range children { 1282 if !child.IsRunning() { 1283 return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias) 1284 } 1285 1286 link, err := links.NewLink( 1287 container.NetworkSettings.IPAddress, 1288 child.NetworkSettings.IPAddress, 1289 linkAlias, 1290 child.Config.Env, 1291 child.Config.ExposedPorts, 1292 daemon.eng) 1293 1294 if err != nil { 1295 rollback() 1296 return nil, err 1297 } 1298 1299 container.activeLinks[link.Alias()] = link 1300 if err := link.Enable(); err != nil { 1301 rollback() 1302 return nil, err 1303 } 1304 1305 for _, envVar := range link.ToEnv() { 1306 env = append(env, envVar) 1307 } 1308 } 1309 } 1310 return env, nil 1311 } 1312 1313 func (container *Container) createDaemonEnvironment(linkedEnv []string) []string { 1314 // if a domain name was specified, append it to the hostname (see #7851) 1315 fullHostname := container.Config.Hostname 1316 if container.Config.Domainname != "" { 1317 fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) 1318 } 1319 // Setup environment 1320 env := []string{ 1321 "PATH=" + DefaultPathEnv, 1322 "HOSTNAME=" + fullHostname, 1323 // Note: we don't set HOME here because it'll get autoset intelligently 1324 // based on the value of USER inside dockerinit, but only if it isn't 1325 // set already (ie, that can be overridden by setting HOME via -e or ENV 1326 // in a Dockerfile). 1327 } 1328 if container.Config.Tty { 1329 env = append(env, "TERM=xterm") 1330 } 1331 env = append(env, linkedEnv...) 1332 // because the env on the container can override certain default values 1333 // we need to replace the 'env' keys where they match and append anything 1334 // else. 1335 env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) 1336 1337 return env 1338 } 1339 1340 func (container *Container) setupWorkingDirectory() error { 1341 if container.Config.WorkingDir != "" { 1342 container.Config.WorkingDir = path.Clean(container.Config.WorkingDir) 1343 1344 pth, err := container.getResourcePath(container.Config.WorkingDir) 1345 if err != nil { 1346 return err 1347 } 1348 1349 pthInfo, err := os.Stat(pth) 1350 if err != nil { 1351 if !os.IsNotExist(err) { 1352 return err 1353 } 1354 1355 if err := os.MkdirAll(pth, 0755); err != nil { 1356 return err 1357 } 1358 } 1359 if pthInfo != nil && !pthInfo.IsDir() { 1360 return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir) 1361 } 1362 } 1363 return nil 1364 } 1365 1366 func (container *Container) startLogging() error { 1367 cfg := container.hostConfig.LogConfig 1368 if cfg.Type == "" { 1369 cfg = container.daemon.defaultLogConfig 1370 } 1371 var l logger.Logger 1372 switch cfg.Type { 1373 case "json-file": 1374 pth, err := container.logPath("json") 1375 if err != nil { 1376 return err 1377 } 1378 1379 dl, err := jsonfilelog.New(pth) 1380 if err != nil { 1381 return err 1382 } 1383 l = dl 1384 case "syslog": 1385 dl, err := syslog.New(container.ID[:12]) 1386 if err != nil { 1387 return err 1388 } 1389 l = dl 1390 case "none": 1391 return nil 1392 default: 1393 return fmt.Errorf("Unknown logging driver: %s", cfg.Type) 1394 } 1395 1396 copier, err := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) 1397 if err != nil { 1398 return err 1399 } 1400 container.logCopier = copier 1401 copier.Run() 1402 container.logDriver = l 1403 1404 return nil 1405 } 1406 1407 func (container *Container) waitForStart() error { 1408 container.monitor = newContainerMonitor(container, container.hostConfig.RestartPolicy) 1409 1410 // block until we either receive an error from the initial start of the container's 1411 // process or until the process is running in the container 1412 select { 1413 case <-container.monitor.startSignal: 1414 case err := <-promise.Go(container.monitor.Start): 1415 return err 1416 } 1417 1418 return nil 1419 } 1420 1421 func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bindings nat.PortMap) error { 1422 binding := bindings[port] 1423 if container.hostConfig.PublishAllPorts && len(binding) == 0 { 1424 binding = append(binding, nat.PortBinding{}) 1425 } 1426 1427 for i := 0; i < len(binding); i++ { 1428 b := binding[i] 1429 1430 job := eng.Job("allocate_port", container.ID) 1431 job.Setenv("HostIP", b.HostIp) 1432 job.Setenv("HostPort", b.HostPort) 1433 job.Setenv("Proto", port.Proto()) 1434 job.Setenv("ContainerPort", port.Port()) 1435 1436 portEnv, err := job.Stdout.AddEnv() 1437 if err != nil { 1438 return err 1439 } 1440 if err := job.Run(); err != nil { 1441 return err 1442 } 1443 b.HostIp = portEnv.Get("HostIP") 1444 b.HostPort = portEnv.Get("HostPort") 1445 1446 binding[i] = b 1447 } 1448 bindings[port] = binding 1449 return nil 1450 } 1451 1452 func (container *Container) GetProcessLabel() string { 1453 // even if we have a process label return "" if we are running 1454 // in privileged mode 1455 if container.hostConfig.Privileged { 1456 return "" 1457 } 1458 return container.ProcessLabel 1459 } 1460 1461 func (container *Container) GetMountLabel() string { 1462 if container.hostConfig.Privileged { 1463 return "" 1464 } 1465 return container.MountLabel 1466 } 1467 1468 func (container *Container) getIpcContainer() (*Container, error) { 1469 containerID := container.hostConfig.IpcMode.Container() 1470 c, err := container.daemon.Get(containerID) 1471 if err != nil { 1472 return nil, err 1473 } 1474 if !c.IsRunning() { 1475 return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID) 1476 } 1477 return c, nil 1478 } 1479 1480 func (container *Container) getNetworkedContainer() (*Container, error) { 1481 parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2) 1482 switch parts[0] { 1483 case "container": 1484 if len(parts) != 2 { 1485 return nil, fmt.Errorf("no container specified to join network") 1486 } 1487 nc, err := container.daemon.Get(parts[1]) 1488 if err != nil { 1489 return nil, err 1490 } 1491 if !nc.IsRunning() { 1492 return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1]) 1493 } 1494 return nc, nil 1495 default: 1496 return nil, fmt.Errorf("network mode not set to container") 1497 } 1498 } 1499 1500 func (container *Container) Stats() (*execdriver.ResourceStats, error) { 1501 return container.daemon.Stats(container) 1502 } 1503 1504 func (c *Container) LogDriverType() string { 1505 c.Lock() 1506 defer c.Unlock() 1507 if c.hostConfig.LogConfig.Type == "" { 1508 return c.daemon.defaultLogConfig.Type 1509 } 1510 return c.hostConfig.LogConfig.Type 1511 }