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  }