github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/container/container_unix.go (about) 1 // +build linux freebsd 2 3 package container 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 "syscall" 12 13 "github.com/Sirupsen/logrus" 14 containertypes "github.com/docker/docker/api/types/container" 15 mounttypes "github.com/docker/docker/api/types/mount" 16 "github.com/docker/docker/pkg/chrootarchive" 17 "github.com/docker/docker/pkg/stringid" 18 "github.com/docker/docker/pkg/symlink" 19 "github.com/docker/docker/pkg/system" 20 "github.com/docker/docker/utils" 21 "github.com/docker/docker/volume" 22 "github.com/opencontainers/runc/libcontainer/label" 23 ) 24 25 // DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container 26 const DefaultSHMSize int64 = 67108864 27 28 // Container holds the fields specific to unixen implementations. 29 // See CommonContainer for standard fields common to all containers. 30 type Container struct { 31 CommonContainer 32 33 // Fields below here are platform specific. 34 AppArmorProfile string 35 HostnamePath string 36 HostsPath string 37 ShmPath string 38 ResolvConfPath string 39 SeccompProfile string 40 NoNewPrivileges bool 41 } 42 43 // ExitStatus provides exit reasons for a container. 44 type ExitStatus struct { 45 // The exit code with which the container exited. 46 ExitCode int 47 48 // Whether the container encountered an OOM. 49 OOMKilled bool 50 } 51 52 // CreateDaemonEnvironment returns the list of all environment variables given the list of 53 // environment variables related to links. 54 // Sets PATH, HOSTNAME and if container.Config.Tty is set: TERM. 55 // The defaults set here do not override the values in container.Config.Env 56 func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string { 57 // Setup environment 58 env := []string{ 59 "PATH=" + system.DefaultPathEnv, 60 "HOSTNAME=" + container.Config.Hostname, 61 } 62 if tty { 63 env = append(env, "TERM=xterm") 64 } 65 env = append(env, linkedEnv...) 66 // because the env on the container can override certain default values 67 // we need to replace the 'env' keys where they match and append anything 68 // else. 69 env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) 70 return env 71 } 72 73 // TrySetNetworkMount attempts to set the network mounts given a provided destination and 74 // the path to use for it; return true if the given destination was a network mount file 75 func (container *Container) TrySetNetworkMount(destination string, path string) bool { 76 if destination == "/etc/resolv.conf" { 77 container.ResolvConfPath = path 78 return true 79 } 80 if destination == "/etc/hostname" { 81 container.HostnamePath = path 82 return true 83 } 84 if destination == "/etc/hosts" { 85 container.HostsPath = path 86 return true 87 } 88 89 return false 90 } 91 92 // BuildHostnameFile writes the container's hostname file. 93 func (container *Container) BuildHostnameFile() error { 94 hostnamePath, err := container.GetRootResourcePath("hostname") 95 if err != nil { 96 return err 97 } 98 container.HostnamePath = hostnamePath 99 return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) 100 } 101 102 // appendNetworkMounts appends any network mounts to the array of mount points passed in 103 func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) { 104 for _, mnt := range container.NetworkMounts() { 105 dest, err := container.GetResourcePath(mnt.Destination) 106 if err != nil { 107 return nil, err 108 } 109 volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest}) 110 } 111 return volumeMounts, nil 112 } 113 114 // NetworkMounts returns the list of network mounts. 115 func (container *Container) NetworkMounts() []Mount { 116 var mounts []Mount 117 shared := container.HostConfig.NetworkMode.IsContainer() 118 if container.ResolvConfPath != "" { 119 if _, err := os.Stat(container.ResolvConfPath); err != nil { 120 logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err) 121 } else { 122 if !container.HasMountFor("/etc/resolv.conf") { 123 label.Relabel(container.ResolvConfPath, container.MountLabel, shared) 124 } 125 writable := !container.HostConfig.ReadonlyRootfs 126 if m, exists := container.MountPoints["/etc/resolv.conf"]; exists { 127 writable = m.RW 128 } 129 mounts = append(mounts, Mount{ 130 Source: container.ResolvConfPath, 131 Destination: "/etc/resolv.conf", 132 Writable: writable, 133 Propagation: string(volume.DefaultPropagationMode), 134 }) 135 } 136 } 137 if container.HostnamePath != "" { 138 if _, err := os.Stat(container.HostnamePath); err != nil { 139 logrus.Warnf("HostnamePath set to %q, but can't stat this filename (err = %v); skipping", container.HostnamePath, err) 140 } else { 141 if !container.HasMountFor("/etc/hostname") { 142 label.Relabel(container.HostnamePath, container.MountLabel, shared) 143 } 144 writable := !container.HostConfig.ReadonlyRootfs 145 if m, exists := container.MountPoints["/etc/hostname"]; exists { 146 writable = m.RW 147 } 148 mounts = append(mounts, Mount{ 149 Source: container.HostnamePath, 150 Destination: "/etc/hostname", 151 Writable: writable, 152 Propagation: string(volume.DefaultPropagationMode), 153 }) 154 } 155 } 156 if container.HostsPath != "" { 157 if _, err := os.Stat(container.HostsPath); err != nil { 158 logrus.Warnf("HostsPath set to %q, but can't stat this filename (err = %v); skipping", container.HostsPath, err) 159 } else { 160 if !container.HasMountFor("/etc/hosts") { 161 label.Relabel(container.HostsPath, container.MountLabel, shared) 162 } 163 writable := !container.HostConfig.ReadonlyRootfs 164 if m, exists := container.MountPoints["/etc/hosts"]; exists { 165 writable = m.RW 166 } 167 mounts = append(mounts, Mount{ 168 Source: container.HostsPath, 169 Destination: "/etc/hosts", 170 Writable: writable, 171 Propagation: string(volume.DefaultPropagationMode), 172 }) 173 } 174 } 175 return mounts 176 } 177 178 // CopyImagePathContent copies files in destination to the volume. 179 func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error { 180 rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS) 181 if err != nil { 182 return err 183 } 184 185 if _, err = ioutil.ReadDir(rootfs); err != nil { 186 if os.IsNotExist(err) { 187 return nil 188 } 189 return err 190 } 191 192 id := stringid.GenerateNonCryptoID() 193 path, err := v.Mount(id) 194 if err != nil { 195 return err 196 } 197 198 defer func() { 199 if err := v.Unmount(id); err != nil { 200 logrus.Warnf("error while unmounting volume %s: %v", v.Name(), err) 201 } 202 }() 203 if err := label.Relabel(path, container.MountLabel, true); err != nil && err != syscall.ENOTSUP { 204 return err 205 } 206 return copyExistingContents(rootfs, path) 207 } 208 209 // ShmResourcePath returns path to shm 210 func (container *Container) ShmResourcePath() (string, error) { 211 return container.GetRootResourcePath("shm") 212 } 213 214 // HasMountFor checks if path is a mountpoint 215 func (container *Container) HasMountFor(path string) bool { 216 _, exists := container.MountPoints[path] 217 return exists 218 } 219 220 // UnmountIpcMounts uses the provided unmount function to unmount shm and mqueue if they were mounted 221 func (container *Container) UnmountIpcMounts(unmount func(pth string) error) { 222 if container.HostConfig.IpcMode.IsContainer() || container.HostConfig.IpcMode.IsHost() { 223 return 224 } 225 226 var warnings []string 227 228 if !container.HasMountFor("/dev/shm") { 229 shmPath, err := container.ShmResourcePath() 230 if err != nil { 231 logrus.Error(err) 232 warnings = append(warnings, err.Error()) 233 } else if shmPath != "" { 234 if err := unmount(shmPath); err != nil && !os.IsNotExist(err) { 235 warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err)) 236 } 237 238 } 239 } 240 241 if len(warnings) > 0 { 242 logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n")) 243 } 244 } 245 246 // IpcMounts returns the list of IPC mounts 247 func (container *Container) IpcMounts() []Mount { 248 var mounts []Mount 249 250 if !container.HasMountFor("/dev/shm") { 251 label.SetFileLabel(container.ShmPath, container.MountLabel) 252 mounts = append(mounts, Mount{ 253 Source: container.ShmPath, 254 Destination: "/dev/shm", 255 Writable: true, 256 Propagation: string(volume.DefaultPropagationMode), 257 }) 258 } 259 260 return mounts 261 } 262 263 // UpdateContainer updates configuration of a container. 264 func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error { 265 container.Lock() 266 defer container.Unlock() 267 268 // update resources of container 269 resources := hostConfig.Resources 270 cResources := &container.HostConfig.Resources 271 if resources.BlkioWeight != 0 { 272 cResources.BlkioWeight = resources.BlkioWeight 273 } 274 if resources.CPUShares != 0 { 275 cResources.CPUShares = resources.CPUShares 276 } 277 if resources.CPUPeriod != 0 { 278 cResources.CPUPeriod = resources.CPUPeriod 279 } 280 if resources.CPUQuota != 0 { 281 cResources.CPUQuota = resources.CPUQuota 282 } 283 if resources.CpusetCpus != "" { 284 cResources.CpusetCpus = resources.CpusetCpus 285 } 286 if resources.CpusetMems != "" { 287 cResources.CpusetMems = resources.CpusetMems 288 } 289 if resources.Memory != 0 { 290 // if memory limit smaller than already set memoryswap limit and doesn't 291 // update the memoryswap limit, then error out. 292 if resources.Memory > cResources.MemorySwap && resources.MemorySwap == 0 { 293 return fmt.Errorf("Memory limit should be smaller than already set memoryswap limit, update the memoryswap at the same time") 294 } 295 cResources.Memory = resources.Memory 296 } 297 if resources.MemorySwap != 0 { 298 cResources.MemorySwap = resources.MemorySwap 299 } 300 if resources.MemoryReservation != 0 { 301 cResources.MemoryReservation = resources.MemoryReservation 302 } 303 if resources.KernelMemory != 0 { 304 cResources.KernelMemory = resources.KernelMemory 305 } 306 307 // update HostConfig of container 308 if hostConfig.RestartPolicy.Name != "" { 309 if container.HostConfig.AutoRemove && !hostConfig.RestartPolicy.IsNone() { 310 return fmt.Errorf("Restart policy cannot be updated because AutoRemove is enabled for the container") 311 } 312 container.HostConfig.RestartPolicy = hostConfig.RestartPolicy 313 } 314 315 if err := container.ToDisk(); err != nil { 316 logrus.Errorf("Error saving updated container: %v", err) 317 return err 318 } 319 320 return nil 321 } 322 323 func detachMounted(path string) error { 324 return syscall.Unmount(path, syscall.MNT_DETACH) 325 } 326 327 // UnmountVolumes unmounts all volumes 328 func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error { 329 var ( 330 volumeMounts []volume.MountPoint 331 err error 332 ) 333 334 for _, mntPoint := range container.MountPoints { 335 dest, err := container.GetResourcePath(mntPoint.Destination) 336 if err != nil { 337 return err 338 } 339 340 volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest, Volume: mntPoint.Volume, ID: mntPoint.ID}) 341 } 342 343 // Append any network mounts to the list (this is a no-op on Windows) 344 if volumeMounts, err = appendNetworkMounts(container, volumeMounts); err != nil { 345 return err 346 } 347 348 for _, volumeMount := range volumeMounts { 349 if forceSyscall { 350 if err := detachMounted(volumeMount.Destination); err != nil { 351 logrus.Warnf("%s unmountVolumes: Failed to do lazy umount %v", container.ID, err) 352 } 353 } 354 355 if volumeMount.Volume != nil { 356 if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil { 357 return err 358 } 359 volumeMount.ID = "" 360 361 attributes := map[string]string{ 362 "driver": volumeMount.Volume.DriverName(), 363 "container": container.ID, 364 } 365 volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes) 366 } 367 } 368 369 return nil 370 } 371 372 // copyExistingContents copies from the source to the destination and 373 // ensures the ownership is appropriately set. 374 func copyExistingContents(source, destination string) error { 375 volList, err := ioutil.ReadDir(source) 376 if err != nil { 377 return err 378 } 379 if len(volList) > 0 { 380 srcList, err := ioutil.ReadDir(destination) 381 if err != nil { 382 return err 383 } 384 if len(srcList) == 0 { 385 // If the source volume is empty, copies files from the root into the volume 386 if err := chrootarchive.CopyWithTar(source, destination); err != nil { 387 return err 388 } 389 } 390 } 391 return copyOwnership(source, destination) 392 } 393 394 // copyOwnership copies the permissions and uid:gid of the source file 395 // to the destination file 396 func copyOwnership(source, destination string) error { 397 stat, err := system.Stat(source) 398 if err != nil { 399 return err 400 } 401 402 if err := os.Chown(destination, int(stat.UID()), int(stat.GID())); err != nil { 403 return err 404 } 405 406 return os.Chmod(destination, os.FileMode(stat.Mode())) 407 } 408 409 // TmpfsMounts returns the list of tmpfs mounts 410 func (container *Container) TmpfsMounts() ([]Mount, error) { 411 var mounts []Mount 412 for dest, data := range container.HostConfig.Tmpfs { 413 mounts = append(mounts, Mount{ 414 Source: "tmpfs", 415 Destination: dest, 416 Data: data, 417 }) 418 } 419 for dest, mnt := range container.MountPoints { 420 if mnt.Type == mounttypes.TypeTmpfs { 421 data, err := volume.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions) 422 if err != nil { 423 return nil, err 424 } 425 mounts = append(mounts, Mount{ 426 Source: "tmpfs", 427 Destination: dest, 428 Data: data, 429 }) 430 } 431 } 432 return mounts, nil 433 } 434 435 // cleanResourcePath cleans a resource path and prepares to combine with mnt path 436 func cleanResourcePath(path string) string { 437 return filepath.Join(string(os.PathSeparator), path) 438 } 439 440 // canMountFS determines if the file system for the container 441 // can be mounted locally. A no-op on non-Windows platforms 442 func (container *Container) canMountFS() bool { 443 return true 444 } 445 446 // EnableServiceDiscoveryOnDefaultNetwork Enable service discovery on default network 447 func (container *Container) EnableServiceDiscoveryOnDefaultNetwork() bool { 448 return false 449 }