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