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