github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/container/container_unix.go (about) 1 // +build linux freebsd 2 3 package container 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "net" 9 "os" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "syscall" 14 15 "github.com/Sirupsen/logrus" 16 "github.com/docker/docker/api/types/network" 17 "github.com/docker/docker/daemon/execdriver" 18 derr "github.com/docker/docker/errors" 19 "github.com/docker/docker/pkg/chrootarchive" 20 "github.com/docker/docker/pkg/nat" 21 "github.com/docker/docker/pkg/symlink" 22 "github.com/docker/docker/pkg/system" 23 "github.com/docker/docker/utils" 24 "github.com/docker/docker/volume" 25 "github.com/docker/libnetwork" 26 "github.com/docker/libnetwork/netlabel" 27 "github.com/docker/libnetwork/options" 28 "github.com/docker/libnetwork/types" 29 "github.com/opencontainers/runc/libcontainer/label" 30 ) 31 32 // DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container 33 const DefaultSHMSize int64 = 67108864 34 35 // Container holds the fields specific to unixen implementations. See 36 // CommonContainer for standard fields common to all containers. 37 type Container struct { 38 CommonContainer 39 40 // Fields below here are platform specific. 41 AppArmorProfile string 42 HostnamePath string 43 HostsPath string 44 ShmPath string 45 MqueuePath string 46 ResolvConfPath string 47 SeccompProfile string 48 } 49 50 // CreateDaemonEnvironment returns the list of all environment variables given the list of 51 // environment variables related to links. 52 // Sets PATH, HOSTNAME and if container.Config.Tty is set: TERM. 53 // The defaults set here do not override the values in container.Config.Env 54 func (container *Container) CreateDaemonEnvironment(linkedEnv []string) []string { 55 // if a domain name was specified, append it to the hostname (see #7851) 56 fullHostname := container.Config.Hostname 57 if container.Config.Domainname != "" { 58 fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) 59 } 60 // Setup environment 61 env := []string{ 62 "PATH=" + system.DefaultPathEnv, 63 "HOSTNAME=" + fullHostname, 64 // Note: we don't set HOME here because it'll get autoset intelligently 65 // based on the value of USER inside dockerinit, but only if it isn't 66 // set already (ie, that can be overridden by setting HOME via -e or ENV 67 // in a Dockerfile). 68 } 69 if container.Config.Tty { 70 env = append(env, "TERM=xterm") 71 } 72 env = append(env, linkedEnv...) 73 // because the env on the container can override certain default values 74 // we need to replace the 'env' keys where they match and append anything 75 // else. 76 env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) 77 78 return env 79 } 80 81 // TrySetNetworkMount attempts to set the network mounts given a provided destination and 82 // the path to use for it; return true if the given destination was a network mount file 83 func (container *Container) TrySetNetworkMount(destination string, path string) bool { 84 if destination == "/etc/resolv.conf" { 85 container.ResolvConfPath = path 86 return true 87 } 88 if destination == "/etc/hostname" { 89 container.HostnamePath = path 90 return true 91 } 92 if destination == "/etc/hosts" { 93 container.HostsPath = path 94 return true 95 } 96 97 return false 98 } 99 100 // BuildHostnameFile writes the container's hostname file. 101 func (container *Container) BuildHostnameFile() error { 102 hostnamePath, err := container.GetRootResourcePath("hostname") 103 if err != nil { 104 return err 105 } 106 container.HostnamePath = hostnamePath 107 108 if container.Config.Domainname != "" { 109 return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) 110 } 111 return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) 112 } 113 114 // GetEndpointInNetwork returns the container's endpoint to the provided network. 115 func (container *Container) GetEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) { 116 endpointName := strings.TrimPrefix(container.Name, "/") 117 return n.EndpointByName(endpointName) 118 } 119 120 func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error { 121 if ep == nil { 122 return derr.ErrorCodeEmptyEndpoint 123 } 124 125 networkSettings := container.NetworkSettings 126 if networkSettings == nil { 127 return derr.ErrorCodeEmptyNetwork 128 } 129 130 driverInfo, err := ep.DriverInfo() 131 if err != nil { 132 return err 133 } 134 135 if driverInfo == nil { 136 // It is not an error for epInfo to be nil 137 return nil 138 } 139 140 if networkSettings.Ports == nil { 141 networkSettings.Ports = nat.PortMap{} 142 } 143 144 if expData, ok := driverInfo[netlabel.ExposedPorts]; ok { 145 if exposedPorts, ok := expData.([]types.TransportPort); ok { 146 for _, tp := range exposedPorts { 147 natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) 148 if err != nil { 149 return derr.ErrorCodeParsingPort.WithArgs(tp.Port, err) 150 } 151 networkSettings.Ports[natPort] = nil 152 } 153 } 154 } 155 156 mapData, ok := driverInfo[netlabel.PortMap] 157 if !ok { 158 return nil 159 } 160 161 if portMapping, ok := mapData.([]types.PortBinding); ok { 162 for _, pp := range portMapping { 163 natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) 164 if err != nil { 165 return err 166 } 167 natBndg := nat.PortBinding{HostIP: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))} 168 networkSettings.Ports[natPort] = append(networkSettings.Ports[natPort], natBndg) 169 } 170 } 171 172 return nil 173 } 174 175 // BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint. 176 func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { 177 if ep == nil { 178 return derr.ErrorCodeEmptyEndpoint 179 } 180 181 networkSettings := container.NetworkSettings 182 if networkSettings == nil { 183 return derr.ErrorCodeEmptyNetwork 184 } 185 186 epInfo := ep.Info() 187 if epInfo == nil { 188 // It is not an error to get an empty endpoint info 189 return nil 190 } 191 192 if _, ok := networkSettings.Networks[n.Name()]; !ok { 193 networkSettings.Networks[n.Name()] = new(network.EndpointSettings) 194 } 195 networkSettings.Networks[n.Name()].EndpointID = ep.ID() 196 197 iface := epInfo.Iface() 198 if iface == nil { 199 return nil 200 } 201 202 if iface.MacAddress() != nil { 203 networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String() 204 } 205 206 if iface.Address() != nil { 207 ones, _ := iface.Address().Mask.Size() 208 networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String() 209 networkSettings.Networks[n.Name()].IPPrefixLen = ones 210 } 211 212 if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil { 213 onesv6, _ := iface.AddressIPv6().Mask.Size() 214 networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String() 215 networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6 216 } 217 218 return nil 219 } 220 221 // UpdateJoinInfo updates network settings when container joins network n with endpoint ep. 222 func (container *Container) UpdateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { 223 if err := container.buildPortMapInfo(ep); err != nil { 224 return err 225 } 226 227 epInfo := ep.Info() 228 if epInfo == nil { 229 // It is not an error to get an empty endpoint info 230 return nil 231 } 232 if epInfo.Gateway() != nil { 233 container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String() 234 } 235 if epInfo.GatewayIPv6().To16() != nil { 236 container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String() 237 } 238 239 return nil 240 } 241 242 // UpdateSandboxNetworkSettings updates the sandbox ID and Key. 243 func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox) error { 244 container.NetworkSettings.SandboxID = sb.ID() 245 container.NetworkSettings.SandboxKey = sb.Key() 246 return nil 247 } 248 249 // BuildCreateEndpointOptions builds endpoint options from a given network. 250 func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) { 251 var ( 252 portSpecs = make(nat.PortSet) 253 bindings = make(nat.PortMap) 254 pbList []types.PortBinding 255 exposeList []types.TransportPort 256 createOptions []libnetwork.EndpointOption 257 ) 258 259 if n.Name() == "bridge" || container.NetworkSettings.IsAnonymousEndpoint { 260 createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) 261 } 262 263 // Other configs are applicable only for the endpoint in the network 264 // to which container was connected to on docker run. 265 if n.Name() != container.HostConfig.NetworkMode.NetworkName() && 266 !(n.Name() == "bridge" && container.HostConfig.NetworkMode.IsDefault()) { 267 return createOptions, nil 268 } 269 270 if container.Config.ExposedPorts != nil { 271 portSpecs = container.Config.ExposedPorts 272 } 273 274 if container.HostConfig.PortBindings != nil { 275 for p, b := range container.HostConfig.PortBindings { 276 bindings[p] = []nat.PortBinding{} 277 for _, bb := range b { 278 bindings[p] = append(bindings[p], nat.PortBinding{ 279 HostIP: bb.HostIP, 280 HostPort: bb.HostPort, 281 }) 282 } 283 } 284 } 285 286 ports := make([]nat.Port, len(portSpecs)) 287 var i int 288 for p := range portSpecs { 289 ports[i] = p 290 i++ 291 } 292 nat.SortPortMap(ports, bindings) 293 for _, port := range ports { 294 expose := types.TransportPort{} 295 expose.Proto = types.ParseProtocol(port.Proto()) 296 expose.Port = uint16(port.Int()) 297 exposeList = append(exposeList, expose) 298 299 pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} 300 binding := bindings[port] 301 for i := 0; i < len(binding); i++ { 302 pbCopy := pb.GetCopy() 303 newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) 304 var portStart, portEnd int 305 if err == nil { 306 portStart, portEnd, err = newP.Range() 307 } 308 if err != nil { 309 return nil, derr.ErrorCodeHostPort.WithArgs(binding[i].HostPort, err) 310 } 311 pbCopy.HostPort = uint16(portStart) 312 pbCopy.HostPortEnd = uint16(portEnd) 313 pbCopy.HostIP = net.ParseIP(binding[i].HostIP) 314 pbList = append(pbList, pbCopy) 315 } 316 317 if container.HostConfig.PublishAllPorts && len(binding) == 0 { 318 pbList = append(pbList, pb) 319 } 320 } 321 322 createOptions = append(createOptions, 323 libnetwork.CreateOptionPortMapping(pbList), 324 libnetwork.CreateOptionExposedPorts(exposeList)) 325 326 if container.Config.MacAddress != "" { 327 mac, err := net.ParseMAC(container.Config.MacAddress) 328 if err != nil { 329 return nil, err 330 } 331 332 genericOption := options.Generic{ 333 netlabel.MacAddress: mac, 334 } 335 336 createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) 337 } 338 339 return createOptions, nil 340 } 341 342 // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir 343 func (container *Container) SetupWorkingDirectory() error { 344 if container.Config.WorkingDir == "" { 345 return nil 346 } 347 container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir) 348 349 pth, err := container.GetResourcePath(container.Config.WorkingDir) 350 if err != nil { 351 return err 352 } 353 354 pthInfo, err := os.Stat(pth) 355 if err != nil { 356 if !os.IsNotExist(err) { 357 return err 358 } 359 360 if err := system.MkdirAll(pth, 0755); err != nil { 361 return err 362 } 363 } 364 if pthInfo != nil && !pthInfo.IsDir() { 365 return derr.ErrorCodeNotADir.WithArgs(container.Config.WorkingDir) 366 } 367 return nil 368 } 369 370 // appendNetworkMounts appends any network mounts to the array of mount points passed in 371 func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) { 372 for _, mnt := range container.NetworkMounts() { 373 dest, err := container.GetResourcePath(mnt.Destination) 374 if err != nil { 375 return nil, err 376 } 377 volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest}) 378 } 379 return volumeMounts, nil 380 } 381 382 // NetworkMounts returns the list of network mounts. 383 func (container *Container) NetworkMounts() []execdriver.Mount { 384 var mounts []execdriver.Mount 385 shared := container.HostConfig.NetworkMode.IsContainer() 386 if container.ResolvConfPath != "" { 387 if _, err := os.Stat(container.ResolvConfPath); err != nil { 388 logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err) 389 } else { 390 label.Relabel(container.ResolvConfPath, container.MountLabel, shared) 391 writable := !container.HostConfig.ReadonlyRootfs 392 if m, exists := container.MountPoints["/etc/resolv.conf"]; exists { 393 writable = m.RW 394 } 395 mounts = append(mounts, execdriver.Mount{ 396 Source: container.ResolvConfPath, 397 Destination: "/etc/resolv.conf", 398 Writable: writable, 399 Propagation: volume.DefaultPropagationMode, 400 }) 401 } 402 } 403 if container.HostnamePath != "" { 404 if _, err := os.Stat(container.HostnamePath); err != nil { 405 logrus.Warnf("HostnamePath set to %q, but can't stat this filename (err = %v); skipping", container.HostnamePath, err) 406 } else { 407 label.Relabel(container.HostnamePath, container.MountLabel, shared) 408 writable := !container.HostConfig.ReadonlyRootfs 409 if m, exists := container.MountPoints["/etc/hostname"]; exists { 410 writable = m.RW 411 } 412 mounts = append(mounts, execdriver.Mount{ 413 Source: container.HostnamePath, 414 Destination: "/etc/hostname", 415 Writable: writable, 416 Propagation: volume.DefaultPropagationMode, 417 }) 418 } 419 } 420 if container.HostsPath != "" { 421 if _, err := os.Stat(container.HostsPath); err != nil { 422 logrus.Warnf("HostsPath set to %q, but can't stat this filename (err = %v); skipping", container.HostsPath, err) 423 } else { 424 label.Relabel(container.HostsPath, container.MountLabel, shared) 425 writable := !container.HostConfig.ReadonlyRootfs 426 if m, exists := container.MountPoints["/etc/hosts"]; exists { 427 writable = m.RW 428 } 429 mounts = append(mounts, execdriver.Mount{ 430 Source: container.HostsPath, 431 Destination: "/etc/hosts", 432 Writable: writable, 433 Propagation: volume.DefaultPropagationMode, 434 }) 435 } 436 } 437 return mounts 438 } 439 440 // CopyImagePathContent copies files in destination to the volume. 441 func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error { 442 rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS) 443 if err != nil { 444 return err 445 } 446 447 if _, err = ioutil.ReadDir(rootfs); err != nil { 448 if os.IsNotExist(err) { 449 return nil 450 } 451 return err 452 } 453 454 path, err := v.Mount() 455 if err != nil { 456 return err 457 } 458 459 if err := copyExistingContents(rootfs, path); err != nil { 460 return err 461 } 462 463 return v.Unmount() 464 } 465 466 // ShmResourcePath returns path to shm 467 func (container *Container) ShmResourcePath() (string, error) { 468 return container.GetRootResourcePath("shm") 469 } 470 471 // MqueueResourcePath returns path to mqueue 472 func (container *Container) MqueueResourcePath() (string, error) { 473 return container.GetRootResourcePath("mqueue") 474 } 475 476 // HasMountFor checks if path is a mountpoint 477 func (container *Container) HasMountFor(path string) bool { 478 _, exists := container.MountPoints[path] 479 return exists 480 } 481 482 // UnmountIpcMounts uses the provided unmount function to unmount shm and mqueue if they were mounted 483 func (container *Container) UnmountIpcMounts(unmount func(pth string) error) { 484 if container.HostConfig.IpcMode.IsContainer() || container.HostConfig.IpcMode.IsHost() { 485 return 486 } 487 488 var warnings []string 489 490 if !container.HasMountFor("/dev/shm") { 491 shmPath, err := container.ShmResourcePath() 492 if err != nil { 493 logrus.Error(err) 494 warnings = append(warnings, err.Error()) 495 } else if shmPath != "" { 496 if err := unmount(shmPath); err != nil { 497 warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err)) 498 } 499 500 } 501 } 502 503 if !container.HasMountFor("/dev/mqueue") { 504 mqueuePath, err := container.MqueueResourcePath() 505 if err != nil { 506 logrus.Error(err) 507 warnings = append(warnings, err.Error()) 508 } else if mqueuePath != "" { 509 if err := unmount(mqueuePath); err != nil { 510 warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", mqueuePath, err)) 511 } 512 } 513 } 514 515 if len(warnings) > 0 { 516 logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n")) 517 } 518 } 519 520 // IpcMounts returns the list of IPC mounts 521 func (container *Container) IpcMounts() []execdriver.Mount { 522 var mounts []execdriver.Mount 523 524 if !container.HasMountFor("/dev/shm") { 525 label.SetFileLabel(container.ShmPath, container.MountLabel) 526 mounts = append(mounts, execdriver.Mount{ 527 Source: container.ShmPath, 528 Destination: "/dev/shm", 529 Writable: true, 530 Propagation: volume.DefaultPropagationMode, 531 }) 532 } 533 534 if !container.HasMountFor("/dev/mqueue") { 535 label.SetFileLabel(container.MqueuePath, container.MountLabel) 536 mounts = append(mounts, execdriver.Mount{ 537 Source: container.MqueuePath, 538 Destination: "/dev/mqueue", 539 Writable: true, 540 Propagation: volume.DefaultPropagationMode, 541 }) 542 } 543 return mounts 544 } 545 546 func detachMounted(path string) error { 547 return syscall.Unmount(path, syscall.MNT_DETACH) 548 } 549 550 // UnmountVolumes unmounts all volumes 551 func (container *Container) UnmountVolumes(forceSyscall bool) error { 552 var ( 553 volumeMounts []volume.MountPoint 554 err error 555 ) 556 557 for _, mntPoint := range container.MountPoints { 558 dest, err := container.GetResourcePath(mntPoint.Destination) 559 if err != nil { 560 return err 561 } 562 563 volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest, Volume: mntPoint.Volume}) 564 } 565 566 // Append any network mounts to the list (this is a no-op on Windows) 567 if volumeMounts, err = appendNetworkMounts(container, volumeMounts); err != nil { 568 return err 569 } 570 571 for _, volumeMount := range volumeMounts { 572 if forceSyscall { 573 if err := detachMounted(volumeMount.Destination); err != nil { 574 logrus.Warnf("%s unmountVolumes: Failed to do lazy umount %v", container.ID, err) 575 } 576 } 577 578 if volumeMount.Volume != nil { 579 if err := volumeMount.Volume.Unmount(); err != nil { 580 return err 581 } 582 } 583 } 584 585 return nil 586 } 587 588 // copyExistingContents copies from the source to the destination and 589 // ensures the ownership is appropriately set. 590 func copyExistingContents(source, destination string) error { 591 volList, err := ioutil.ReadDir(source) 592 if err != nil { 593 return err 594 } 595 if len(volList) > 0 { 596 srcList, err := ioutil.ReadDir(destination) 597 if err != nil { 598 return err 599 } 600 if len(srcList) == 0 { 601 // If the source volume is empty copy files from the root into the volume 602 if err := chrootarchive.CopyWithTar(source, destination); err != nil { 603 return err 604 } 605 } 606 } 607 return copyOwnership(source, destination) 608 } 609 610 // copyOwnership copies the permissions and uid:gid of the source file 611 // to the destination file 612 func copyOwnership(source, destination string) error { 613 stat, err := system.Stat(source) 614 if err != nil { 615 return err 616 } 617 618 if err := os.Chown(destination, int(stat.UID()), int(stat.GID())); err != nil { 619 return err 620 } 621 622 return os.Chmod(destination, os.FileMode(stat.Mode())) 623 } 624 625 // TmpfsMounts returns the list of tmpfs mounts 626 func (container *Container) TmpfsMounts() []execdriver.Mount { 627 var mounts []execdriver.Mount 628 for dest, data := range container.HostConfig.Tmpfs { 629 mounts = append(mounts, execdriver.Mount{ 630 Source: "tmpfs", 631 Destination: dest, 632 Data: data, 633 }) 634 } 635 return mounts 636 }