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