github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/daemon/container_operations_unix.go (about) 1 // +build linux freebsd 2 3 package daemon 4 5 import ( 6 "fmt" 7 "os" 8 "path/filepath" 9 "strconv" 10 "strings" 11 "syscall" 12 "time" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/docker/container" 16 "github.com/docker/docker/daemon/links" 17 "github.com/docker/docker/pkg/fileutils" 18 "github.com/docker/docker/pkg/idtools" 19 "github.com/docker/docker/pkg/mount" 20 "github.com/docker/docker/pkg/stringid" 21 "github.com/docker/docker/runconfig" 22 containertypes "github.com/docker/engine-api/types/container" 23 networktypes "github.com/docker/engine-api/types/network" 24 "github.com/docker/libnetwork" 25 "github.com/opencontainers/runc/libcontainer/configs" 26 "github.com/opencontainers/runc/libcontainer/devices" 27 "github.com/opencontainers/runc/libcontainer/label" 28 "github.com/opencontainers/specs/specs-go" 29 ) 30 31 func u32Ptr(i int64) *uint32 { u := uint32(i); return &u } 32 func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm } 33 34 func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { 35 var env []string 36 children := daemon.children(container) 37 38 bridgeSettings := container.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] 39 if bridgeSettings == nil { 40 return nil, nil 41 } 42 43 for linkAlias, child := range children { 44 if !child.IsRunning() { 45 return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias) 46 } 47 48 childBridgeSettings := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] 49 if childBridgeSettings == nil { 50 return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID) 51 } 52 53 link := links.NewLink( 54 bridgeSettings.IPAddress, 55 childBridgeSettings.IPAddress, 56 linkAlias, 57 child.Config.Env, 58 child.Config.ExposedPorts, 59 ) 60 61 for _, envVar := range link.ToEnv() { 62 env = append(env, envVar) 63 } 64 } 65 66 return env, nil 67 } 68 69 // getSize returns the real size & virtual size of the container. 70 func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { 71 var ( 72 sizeRw, sizeRootfs int64 73 err error 74 ) 75 76 if err := daemon.Mount(container); err != nil { 77 logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) 78 return sizeRw, sizeRootfs 79 } 80 defer daemon.Unmount(container) 81 82 sizeRw, err = container.RWLayer.Size() 83 if err != nil { 84 logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", 85 daemon.GraphDriverName(), container.ID, err) 86 // FIXME: GetSize should return an error. Not changing it now in case 87 // there is a side-effect. 88 sizeRw = -1 89 } 90 91 if parent := container.RWLayer.Parent(); parent != nil { 92 sizeRootfs, err = parent.Size() 93 if err != nil { 94 sizeRootfs = -1 95 } else if sizeRw != -1 { 96 sizeRootfs += sizeRw 97 } 98 } 99 return sizeRw, sizeRootfs 100 } 101 102 // ConnectToNetwork connects a container to a network 103 func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error { 104 if !container.Running { 105 if container.RemovalInProgress || container.Dead { 106 return errRemovalContainer(container.ID) 107 } 108 if _, err := daemon.updateNetworkConfig(container, idOrName, endpointConfig, true); err != nil { 109 return err 110 } 111 if endpointConfig != nil { 112 container.NetworkSettings.Networks[idOrName] = endpointConfig 113 } 114 } else { 115 if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil { 116 return err 117 } 118 } 119 if err := container.ToDiskLocking(); err != nil { 120 return fmt.Errorf("Error saving container to disk: %v", err) 121 } 122 return nil 123 } 124 125 // DisconnectFromNetwork disconnects container from network n. 126 func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error { 127 if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { 128 return runconfig.ErrConflictHostNetwork 129 } 130 if !container.Running { 131 if container.RemovalInProgress || container.Dead { 132 return errRemovalContainer(container.ID) 133 } 134 if _, ok := container.NetworkSettings.Networks[n.Name()]; ok { 135 delete(container.NetworkSettings.Networks, n.Name()) 136 } else { 137 return fmt.Errorf("container %s is not connected to the network %s", container.ID, n.Name()) 138 } 139 } else { 140 if err := disconnectFromNetwork(container, n, false); err != nil { 141 return err 142 } 143 } 144 145 if err := container.ToDiskLocking(); err != nil { 146 return fmt.Errorf("Error saving container to disk: %v", err) 147 } 148 149 attributes := map[string]string{ 150 "container": container.ID, 151 } 152 daemon.LogNetworkEventWithAttributes(n, "disconnect", attributes) 153 return nil 154 } 155 156 func (daemon *Daemon) getIpcContainer(container *container.Container) (*container.Container, error) { 157 containerID := container.HostConfig.IpcMode.Container() 158 c, err := daemon.GetContainer(containerID) 159 if err != nil { 160 return nil, err 161 } 162 if !c.IsRunning() { 163 return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID) 164 } 165 if c.IsRestarting() { 166 return nil, errContainerIsRestarting(container.ID) 167 } 168 return c, nil 169 } 170 171 func (daemon *Daemon) setupIpcDirs(c *container.Container) error { 172 var err error 173 174 c.ShmPath, err = c.ShmResourcePath() 175 if err != nil { 176 return err 177 } 178 179 if c.HostConfig.IpcMode.IsContainer() { 180 ic, err := daemon.getIpcContainer(c) 181 if err != nil { 182 return err 183 } 184 c.ShmPath = ic.ShmPath 185 } else if c.HostConfig.IpcMode.IsHost() { 186 if _, err := os.Stat("/dev/shm"); err != nil { 187 return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host") 188 } 189 c.ShmPath = "/dev/shm" 190 } else { 191 rootUID, rootGID := daemon.GetRemappedUIDGID() 192 if !c.HasMountFor("/dev/shm") { 193 shmPath, err := c.ShmResourcePath() 194 if err != nil { 195 return err 196 } 197 198 if err := idtools.MkdirAllAs(shmPath, 0700, rootUID, rootGID); err != nil { 199 return err 200 } 201 202 shmSize := container.DefaultSHMSize 203 if c.HostConfig.ShmSize != 0 { 204 shmSize = c.HostConfig.ShmSize 205 } 206 shmproperty := "mode=1777,size=" + strconv.FormatInt(shmSize, 10) 207 if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil { 208 return fmt.Errorf("mounting shm tmpfs: %s", err) 209 } 210 if err := os.Chown(shmPath, rootUID, rootGID); err != nil { 211 return err 212 } 213 } 214 215 } 216 217 return nil 218 } 219 220 func (daemon *Daemon) mountVolumes(container *container.Container) error { 221 mounts, err := daemon.setupMounts(container) 222 if err != nil { 223 return err 224 } 225 226 for _, m := range mounts { 227 dest, err := container.GetResourcePath(m.Destination) 228 if err != nil { 229 return err 230 } 231 232 var stat os.FileInfo 233 stat, err = os.Stat(m.Source) 234 if err != nil { 235 return err 236 } 237 if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { 238 return err 239 } 240 241 opts := "rbind,ro" 242 if m.Writable { 243 opts = "rbind,rw" 244 } 245 246 if err := mount.Mount(m.Source, dest, "bind", opts); err != nil { 247 return err 248 } 249 } 250 251 return nil 252 } 253 254 func killProcessDirectly(container *container.Container) error { 255 if _, err := container.WaitStop(10 * time.Second); err != nil { 256 // Ensure that we don't kill ourselves 257 if pid := container.GetPID(); pid != 0 { 258 logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(container.ID)) 259 if err := syscall.Kill(pid, 9); err != nil { 260 if err != syscall.ESRCH { 261 return err 262 } 263 e := errNoSuchProcess{pid, 9} 264 logrus.Debug(e) 265 return e 266 } 267 } 268 } 269 return nil 270 } 271 272 func specDevice(d *configs.Device) specs.Device { 273 return specs.Device{ 274 Type: string(d.Type), 275 Path: d.Path, 276 Major: d.Major, 277 Minor: d.Minor, 278 FileMode: fmPtr(int64(d.FileMode)), 279 UID: u32Ptr(int64(d.Uid)), 280 GID: u32Ptr(int64(d.Gid)), 281 } 282 } 283 284 func specDeviceCgroup(d *configs.Device) specs.DeviceCgroup { 285 t := string(d.Type) 286 return specs.DeviceCgroup{ 287 Allow: true, 288 Type: &t, 289 Major: &d.Major, 290 Minor: &d.Minor, 291 Access: &d.Permissions, 292 } 293 } 294 295 func getDevicesFromPath(deviceMapping containertypes.DeviceMapping) (devs []specs.Device, devPermissions []specs.DeviceCgroup, err error) { 296 resolvedPathOnHost := deviceMapping.PathOnHost 297 298 // check if it is a symbolic link 299 if src, e := os.Lstat(deviceMapping.PathOnHost); e == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink { 300 if linkedPathOnHost, e := os.Readlink(deviceMapping.PathOnHost); e == nil { 301 resolvedPathOnHost = linkedPathOnHost 302 } 303 } 304 305 device, err := devices.DeviceFromPath(resolvedPathOnHost, deviceMapping.CgroupPermissions) 306 // if there was no error, return the device 307 if err == nil { 308 device.Path = deviceMapping.PathInContainer 309 return append(devs, specDevice(device)), append(devPermissions, specDeviceCgroup(device)), nil 310 } 311 312 // if the device is not a device node 313 // try to see if it's a directory holding many devices 314 if err == devices.ErrNotADevice { 315 316 // check if it is a directory 317 if src, e := os.Stat(resolvedPathOnHost); e == nil && src.IsDir() { 318 319 // mount the internal devices recursively 320 filepath.Walk(resolvedPathOnHost, func(dpath string, f os.FileInfo, e error) error { 321 childDevice, e := devices.DeviceFromPath(dpath, deviceMapping.CgroupPermissions) 322 if e != nil { 323 // ignore the device 324 return nil 325 } 326 327 // add the device to userSpecified devices 328 childDevice.Path = strings.Replace(dpath, resolvedPathOnHost, deviceMapping.PathInContainer, 1) 329 devs = append(devs, specDevice(childDevice)) 330 devPermissions = append(devPermissions, specDeviceCgroup(childDevice)) 331 332 return nil 333 }) 334 } 335 } 336 337 if len(devs) > 0 { 338 return devs, devPermissions, nil 339 } 340 341 return devs, devPermissions, fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err) 342 } 343 344 func detachMounted(path string) error { 345 return syscall.Unmount(path, syscall.MNT_DETACH) 346 } 347 348 func isLinkable(child *container.Container) bool { 349 // A container is linkable only if it belongs to the default network 350 _, ok := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] 351 return ok 352 } 353 354 func errRemovalContainer(containerID string) error { 355 return fmt.Errorf("Container %s is marked for removal and cannot be connected or disconnected to the network", containerID) 356 }