github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/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 "github.com/docker/docker/pkg/chrootarchive" 15 "github.com/docker/docker/pkg/stringid" 16 "github.com/docker/docker/pkg/symlink" 17 "github.com/docker/docker/pkg/system" 18 "github.com/docker/docker/utils" 19 "github.com/docker/docker/volume" 20 containertypes "github.com/docker/engine-api/types/container" 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(linkedEnv []string) []string { 56 // Setup environment 57 env := []string{ 58 "PATH=" + system.DefaultPathEnv, 59 "HOSTNAME=" + container.Config.Hostname, 60 } 61 if container.Config.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: 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: 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: 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 return copyExistingContents(rootfs, path) 203 } 204 205 // ShmResourcePath returns path to shm 206 func (container *Container) ShmResourcePath() (string, error) { 207 return container.GetRootResourcePath("shm") 208 } 209 210 // HasMountFor checks if path is a mountpoint 211 func (container *Container) HasMountFor(path string) bool { 212 _, exists := container.MountPoints[path] 213 return exists 214 } 215 216 // UnmountIpcMounts uses the provided unmount function to unmount shm and mqueue if they were mounted 217 func (container *Container) UnmountIpcMounts(unmount func(pth string) error) { 218 if container.HostConfig.IpcMode.IsContainer() || container.HostConfig.IpcMode.IsHost() { 219 return 220 } 221 222 var warnings []string 223 224 if !container.HasMountFor("/dev/shm") { 225 shmPath, err := container.ShmResourcePath() 226 if err != nil { 227 logrus.Error(err) 228 warnings = append(warnings, err.Error()) 229 } else if shmPath != "" { 230 if err := unmount(shmPath); err != nil && !os.IsNotExist(err) { 231 warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err)) 232 } 233 234 } 235 } 236 237 if len(warnings) > 0 { 238 logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n")) 239 } 240 } 241 242 // IpcMounts returns the list of IPC mounts 243 func (container *Container) IpcMounts() []Mount { 244 var mounts []Mount 245 246 if !container.HasMountFor("/dev/shm") { 247 label.SetFileLabel(container.ShmPath, container.MountLabel) 248 mounts = append(mounts, Mount{ 249 Source: container.ShmPath, 250 Destination: "/dev/shm", 251 Writable: true, 252 Propagation: volume.DefaultPropagationMode, 253 }) 254 } 255 256 return mounts 257 } 258 259 // UpdateContainer updates configuration of a container. 260 func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error { 261 container.Lock() 262 defer container.Unlock() 263 264 // update resources of container 265 resources := hostConfig.Resources 266 cResources := &container.HostConfig.Resources 267 if resources.BlkioWeight != 0 { 268 cResources.BlkioWeight = resources.BlkioWeight 269 } 270 if resources.CPUShares != 0 { 271 cResources.CPUShares = resources.CPUShares 272 } 273 if resources.CPUPeriod != 0 { 274 cResources.CPUPeriod = resources.CPUPeriod 275 } 276 if resources.CPUQuota != 0 { 277 cResources.CPUQuota = resources.CPUQuota 278 } 279 if resources.CpusetCpus != "" { 280 cResources.CpusetCpus = resources.CpusetCpus 281 } 282 if resources.CpusetMems != "" { 283 cResources.CpusetMems = resources.CpusetMems 284 } 285 if resources.Memory != 0 { 286 // if memory limit smaller than already set memoryswap limit and doesn't 287 // update the memoryswap limit, then error out. 288 if resources.Memory > cResources.MemorySwap && resources.MemorySwap == 0 { 289 return fmt.Errorf("Memory limit should be smaller than already set memoryswap limit, update the memoryswap at the same time") 290 } 291 cResources.Memory = resources.Memory 292 } 293 if resources.MemorySwap != 0 { 294 cResources.MemorySwap = resources.MemorySwap 295 } 296 if resources.MemoryReservation != 0 { 297 cResources.MemoryReservation = resources.MemoryReservation 298 } 299 if resources.KernelMemory != 0 { 300 cResources.KernelMemory = resources.KernelMemory 301 } 302 303 // update HostConfig of container 304 if hostConfig.RestartPolicy.Name != "" { 305 container.HostConfig.RestartPolicy = hostConfig.RestartPolicy 306 } 307 308 if err := container.ToDisk(); err != nil { 309 logrus.Errorf("Error saving updated container: %v", err) 310 return err 311 } 312 313 return nil 314 } 315 316 func detachMounted(path string) error { 317 return syscall.Unmount(path, syscall.MNT_DETACH) 318 } 319 320 // UnmountVolumes unmounts all volumes 321 func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error { 322 var ( 323 volumeMounts []volume.MountPoint 324 err error 325 ) 326 327 for _, mntPoint := range container.MountPoints { 328 dest, err := container.GetResourcePath(mntPoint.Destination) 329 if err != nil { 330 return err 331 } 332 333 volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest, Volume: mntPoint.Volume, ID: mntPoint.ID}) 334 } 335 336 // Append any network mounts to the list (this is a no-op on Windows) 337 if volumeMounts, err = appendNetworkMounts(container, volumeMounts); err != nil { 338 return err 339 } 340 341 for _, volumeMount := range volumeMounts { 342 if forceSyscall { 343 if err := detachMounted(volumeMount.Destination); err != nil { 344 logrus.Warnf("%s unmountVolumes: Failed to do lazy umount %v", container.ID, err) 345 } 346 } 347 348 if volumeMount.Volume != nil { 349 if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil { 350 return err 351 } 352 volumeMount.ID = "" 353 354 attributes := map[string]string{ 355 "driver": volumeMount.Volume.DriverName(), 356 "container": container.ID, 357 } 358 volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes) 359 } 360 } 361 362 return nil 363 } 364 365 // copyExistingContents copies from the source to the destination and 366 // ensures the ownership is appropriately set. 367 func copyExistingContents(source, destination string) error { 368 volList, err := ioutil.ReadDir(source) 369 if err != nil { 370 return err 371 } 372 if len(volList) > 0 { 373 srcList, err := ioutil.ReadDir(destination) 374 if err != nil { 375 return err 376 } 377 if len(srcList) == 0 { 378 // If the source volume is empty, copies files from the root into the volume 379 if err := chrootarchive.CopyWithTar(source, destination); err != nil { 380 return err 381 } 382 } 383 } 384 return copyOwnership(source, destination) 385 } 386 387 // copyOwnership copies the permissions and uid:gid of the source file 388 // to the destination file 389 func copyOwnership(source, destination string) error { 390 stat, err := system.Stat(source) 391 if err != nil { 392 return err 393 } 394 395 if err := os.Chown(destination, int(stat.UID()), int(stat.GID())); err != nil { 396 return err 397 } 398 399 return os.Chmod(destination, os.FileMode(stat.Mode())) 400 } 401 402 // TmpfsMounts returns the list of tmpfs mounts 403 func (container *Container) TmpfsMounts() []Mount { 404 var mounts []Mount 405 for dest, data := range container.HostConfig.Tmpfs { 406 mounts = append(mounts, Mount{ 407 Source: "tmpfs", 408 Destination: dest, 409 Data: data, 410 }) 411 } 412 return mounts 413 } 414 415 // cleanResourcePath cleans a resource path and prepares to combine with mnt path 416 func cleanResourcePath(path string) string { 417 return filepath.Join(string(os.PathSeparator), path) 418 } 419 420 // canMountFS determines if the file system for the container 421 // can be mounted locally. A no-op on non-Windows platforms 422 func (container *Container) canMountFS() bool { 423 return true 424 }