github.com/portworx/docker@v1.12.1/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 endpointConfig == nil { 105 endpointConfig = &networktypes.EndpointSettings{} 106 } 107 if !container.Running { 108 if container.RemovalInProgress || container.Dead { 109 return errRemovalContainer(container.ID) 110 } 111 if _, err := daemon.updateNetworkConfig(container, idOrName, endpointConfig, true); err != nil { 112 return err 113 } 114 container.NetworkSettings.Networks[idOrName] = endpointConfig 115 } else { 116 if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil { 117 return err 118 } 119 } 120 if err := container.ToDiskLocking(); err != nil { 121 return fmt.Errorf("Error saving container to disk: %v", err) 122 } 123 return nil 124 } 125 126 // DisconnectFromNetwork disconnects container from network n. 127 func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error { 128 if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { 129 return runconfig.ErrConflictHostNetwork 130 } 131 if !container.Running { 132 if container.RemovalInProgress || container.Dead { 133 return errRemovalContainer(container.ID) 134 } 135 if _, ok := container.NetworkSettings.Networks[n.Name()]; ok { 136 delete(container.NetworkSettings.Networks, n.Name()) 137 } else { 138 return fmt.Errorf("container %s is not connected to the network %s", container.ID, n.Name()) 139 } 140 } else { 141 if err := disconnectFromNetwork(container, n, false); err != nil { 142 return err 143 } 144 } 145 146 if err := container.ToDiskLocking(); err != nil { 147 return fmt.Errorf("Error saving container to disk: %v", err) 148 } 149 150 attributes := map[string]string{ 151 "container": container.ID, 152 } 153 daemon.LogNetworkEventWithAttributes(n, "disconnect", attributes) 154 return nil 155 } 156 157 func (daemon *Daemon) getIpcContainer(container *container.Container) (*container.Container, error) { 158 containerID := container.HostConfig.IpcMode.Container() 159 c, err := daemon.GetContainer(containerID) 160 if err != nil { 161 return nil, err 162 } 163 if !c.IsRunning() { 164 return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID) 165 } 166 if c.IsRestarting() { 167 return nil, errContainerIsRestarting(container.ID) 168 } 169 return c, nil 170 } 171 172 func (daemon *Daemon) getPidContainer(container *container.Container) (*container.Container, error) { 173 containerID := container.HostConfig.PidMode.Container() 174 c, err := daemon.GetContainer(containerID) 175 if err != nil { 176 return nil, err 177 } 178 if !c.IsRunning() { 179 return nil, fmt.Errorf("cannot join PID of a non running container: %s", containerID) 180 } 181 if c.IsRestarting() { 182 return nil, errContainerIsRestarting(container.ID) 183 } 184 return c, nil 185 } 186 187 func (daemon *Daemon) setupIpcDirs(c *container.Container) error { 188 var err error 189 190 c.ShmPath, err = c.ShmResourcePath() 191 if err != nil { 192 return err 193 } 194 195 if c.HostConfig.IpcMode.IsContainer() { 196 ic, err := daemon.getIpcContainer(c) 197 if err != nil { 198 return err 199 } 200 c.ShmPath = ic.ShmPath 201 } else if c.HostConfig.IpcMode.IsHost() { 202 if _, err := os.Stat("/dev/shm"); err != nil { 203 return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host") 204 } 205 c.ShmPath = "/dev/shm" 206 } else { 207 rootUID, rootGID := daemon.GetRemappedUIDGID() 208 if !c.HasMountFor("/dev/shm") { 209 shmPath, err := c.ShmResourcePath() 210 if err != nil { 211 return err 212 } 213 214 if err := idtools.MkdirAllAs(shmPath, 0700, rootUID, rootGID); err != nil { 215 return err 216 } 217 218 shmSize := container.DefaultSHMSize 219 if c.HostConfig.ShmSize != 0 { 220 shmSize = c.HostConfig.ShmSize 221 } 222 shmproperty := "mode=1777,size=" + strconv.FormatInt(shmSize, 10) 223 if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil { 224 return fmt.Errorf("mounting shm tmpfs: %s", err) 225 } 226 if err := os.Chown(shmPath, rootUID, rootGID); err != nil { 227 return err 228 } 229 } 230 231 } 232 233 return nil 234 } 235 236 func (daemon *Daemon) mountVolumes(container *container.Container) error { 237 mounts, err := daemon.setupMounts(container) 238 if err != nil { 239 return err 240 } 241 242 for _, m := range mounts { 243 dest, err := container.GetResourcePath(m.Destination) 244 if err != nil { 245 return err 246 } 247 248 var stat os.FileInfo 249 stat, err = os.Stat(m.Source) 250 if err != nil { 251 return err 252 } 253 if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { 254 return err 255 } 256 257 opts := "rbind,ro" 258 if m.Writable { 259 opts = "rbind,rw" 260 } 261 262 if err := mount.Mount(m.Source, dest, "bind", opts); err != nil { 263 return err 264 } 265 266 // mountVolumes() seems to be called for temporary mounts 267 // outside the container. Soon these will be unmounted with 268 // lazy unmount option and given we have mounted the rbind, 269 // all the submounts will propagate if these are shared. If 270 // daemon is running in host namespace and has / as shared 271 // then these unmounts will propagate and unmount original 272 // mount as well. So make all these mounts rprivate. 273 // Do not use propagation property of volume as that should 274 // apply only when mounting happen inside the container. 275 if err := mount.MakeRPrivate(dest); err != nil { 276 return err 277 } 278 } 279 280 return nil 281 } 282 283 func killProcessDirectly(container *container.Container) error { 284 if _, err := container.WaitStop(10 * time.Second); err != nil { 285 // Ensure that we don't kill ourselves 286 if pid := container.GetPID(); pid != 0 { 287 logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(container.ID)) 288 if err := syscall.Kill(pid, 9); err != nil { 289 if err != syscall.ESRCH { 290 return err 291 } 292 e := errNoSuchProcess{pid, 9} 293 logrus.Debug(e) 294 return e 295 } 296 } 297 } 298 return nil 299 } 300 301 func specDevice(d *configs.Device) specs.Device { 302 return specs.Device{ 303 Type: string(d.Type), 304 Path: d.Path, 305 Major: d.Major, 306 Minor: d.Minor, 307 FileMode: fmPtr(int64(d.FileMode)), 308 UID: u32Ptr(int64(d.Uid)), 309 GID: u32Ptr(int64(d.Gid)), 310 } 311 } 312 313 func specDeviceCgroup(d *configs.Device) specs.DeviceCgroup { 314 t := string(d.Type) 315 return specs.DeviceCgroup{ 316 Allow: true, 317 Type: &t, 318 Major: &d.Major, 319 Minor: &d.Minor, 320 Access: &d.Permissions, 321 } 322 } 323 324 func getDevicesFromPath(deviceMapping containertypes.DeviceMapping) (devs []specs.Device, devPermissions []specs.DeviceCgroup, err error) { 325 resolvedPathOnHost := deviceMapping.PathOnHost 326 327 // check if it is a symbolic link 328 if src, e := os.Lstat(deviceMapping.PathOnHost); e == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink { 329 if linkedPathOnHost, e := filepath.EvalSymlinks(deviceMapping.PathOnHost); e == nil { 330 resolvedPathOnHost = linkedPathOnHost 331 } 332 } 333 334 device, err := devices.DeviceFromPath(resolvedPathOnHost, deviceMapping.CgroupPermissions) 335 // if there was no error, return the device 336 if err == nil { 337 device.Path = deviceMapping.PathInContainer 338 return append(devs, specDevice(device)), append(devPermissions, specDeviceCgroup(device)), nil 339 } 340 341 // if the device is not a device node 342 // try to see if it's a directory holding many devices 343 if err == devices.ErrNotADevice { 344 345 // check if it is a directory 346 if src, e := os.Stat(resolvedPathOnHost); e == nil && src.IsDir() { 347 348 // mount the internal devices recursively 349 filepath.Walk(resolvedPathOnHost, func(dpath string, f os.FileInfo, e error) error { 350 childDevice, e := devices.DeviceFromPath(dpath, deviceMapping.CgroupPermissions) 351 if e != nil { 352 // ignore the device 353 return nil 354 } 355 356 // add the device to userSpecified devices 357 childDevice.Path = strings.Replace(dpath, resolvedPathOnHost, deviceMapping.PathInContainer, 1) 358 devs = append(devs, specDevice(childDevice)) 359 devPermissions = append(devPermissions, specDeviceCgroup(childDevice)) 360 361 return nil 362 }) 363 } 364 } 365 366 if len(devs) > 0 { 367 return devs, devPermissions, nil 368 } 369 370 return devs, devPermissions, fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err) 371 } 372 373 func detachMounted(path string) error { 374 return syscall.Unmount(path, syscall.MNT_DETACH) 375 } 376 377 func isLinkable(child *container.Container) bool { 378 // A container is linkable only if it belongs to the default network 379 _, ok := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] 380 return ok 381 } 382 383 func errRemovalContainer(containerID string) error { 384 return fmt.Errorf("Container %s is marked for removal and cannot be connected or disconnected to the network", containerID) 385 }