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