github.com/chenchun/docker@v1.3.2-0.20150629222414-20467faf132b/daemon/daemon_unix.go (about)

     1  // +build !windows
     2  
     3  package daemon
     4  
     5  import (
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  	"syscall"
    14  
    15  	"github.com/Sirupsen/logrus"
    16  	"github.com/docker/docker/autogen/dockerversion"
    17  	"github.com/docker/docker/daemon/graphdriver"
    18  	"github.com/docker/docker/pkg/archive"
    19  	"github.com/docker/docker/pkg/fileutils"
    20  	"github.com/docker/docker/pkg/parsers"
    21  	"github.com/docker/docker/pkg/parsers/kernel"
    22  	"github.com/docker/docker/pkg/system"
    23  	"github.com/docker/docker/runconfig"
    24  	"github.com/docker/docker/utils"
    25  	volumedrivers "github.com/docker/docker/volume/drivers"
    26  	"github.com/docker/docker/volume/local"
    27  	"github.com/docker/libcontainer/label"
    28  	"github.com/docker/libnetwork"
    29  	nwapi "github.com/docker/libnetwork/api"
    30  	nwconfig "github.com/docker/libnetwork/config"
    31  	"github.com/docker/libnetwork/netlabel"
    32  	"github.com/docker/libnetwork/options"
    33  )
    34  
    35  func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
    36  	initID := fmt.Sprintf("%s-init", container.ID)
    37  	return daemon.driver.Changes(container.ID, initID)
    38  }
    39  
    40  func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) {
    41  	initID := fmt.Sprintf("%s-init", container.ID)
    42  	return daemon.driver.Diff(container.ID, initID)
    43  }
    44  
    45  func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error {
    46  	var (
    47  		labelOpts []string
    48  		err       error
    49  	)
    50  
    51  	for _, opt := range config.SecurityOpt {
    52  		con := strings.SplitN(opt, ":", 2)
    53  		if len(con) == 1 {
    54  			return fmt.Errorf("Invalid --security-opt: %q", opt)
    55  		}
    56  		switch con[0] {
    57  		case "label":
    58  			labelOpts = append(labelOpts, con[1])
    59  		case "apparmor":
    60  			container.AppArmorProfile = con[1]
    61  		default:
    62  			return fmt.Errorf("Invalid --security-opt: %q", opt)
    63  		}
    64  	}
    65  
    66  	container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts)
    67  	return err
    68  }
    69  
    70  func (daemon *Daemon) createRootfs(container *Container) error {
    71  	// Step 1: create the container directory.
    72  	// This doubles as a barrier to avoid race conditions.
    73  	if err := os.Mkdir(container.root, 0700); err != nil {
    74  		return err
    75  	}
    76  	initID := fmt.Sprintf("%s-init", container.ID)
    77  	if err := daemon.driver.Create(initID, container.ImageID); err != nil {
    78  		return err
    79  	}
    80  	initPath, err := daemon.driver.Get(initID, "")
    81  	if err != nil {
    82  		return err
    83  	}
    84  	defer daemon.driver.Put(initID)
    85  
    86  	if err := setupInitLayer(initPath); err != nil {
    87  		return err
    88  	}
    89  
    90  	if err := daemon.driver.Create(container.ID, initID); err != nil {
    91  		return err
    92  	}
    93  	return nil
    94  }
    95  
    96  func checkKernel() error {
    97  	// Check for unsupported kernel versions
    98  	// FIXME: it would be cleaner to not test for specific versions, but rather
    99  	// test for specific functionalities.
   100  	// Unfortunately we can't test for the feature "does not cause a kernel panic"
   101  	// without actually causing a kernel panic, so we need this workaround until
   102  	// the circumstances of pre-3.10 crashes are clearer.
   103  	// For details see https://github.com/docker/docker/issues/407
   104  	if k, err := kernel.GetKernelVersion(); err != nil {
   105  		logrus.Warnf("%s", err)
   106  	} else {
   107  		if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 10, Minor: 0}) < 0 {
   108  			if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" {
   109  				logrus.Warnf("You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.10.0.", k.String())
   110  			}
   111  		}
   112  	}
   113  	return nil
   114  }
   115  
   116  func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig, config *runconfig.Config) ([]string, error) {
   117  	var warnings []string
   118  
   119  	if config != nil {
   120  		// The check for a valid workdir path is made on the server rather than in the
   121  		// client. This is because we don't know the type of path (Linux or Windows)
   122  		// to validate on the client.
   123  		if config.WorkingDir != "" && !filepath.IsAbs(config.WorkingDir) {
   124  			return warnings, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path.", config.WorkingDir)
   125  		}
   126  	}
   127  
   128  	if hostConfig == nil {
   129  		return warnings, nil
   130  	}
   131  
   132  	if hostConfig.LxcConf.Len() > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") {
   133  		return warnings, fmt.Errorf("Cannot use --lxc-conf with execdriver: %s", daemon.ExecutionDriver().Name())
   134  	}
   135  	if hostConfig.Memory != 0 && hostConfig.Memory < 4194304 {
   136  		return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB")
   137  	}
   138  	if hostConfig.Memory > 0 && !daemon.SystemConfig().MemoryLimit {
   139  		warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.")
   140  		logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.")
   141  		hostConfig.Memory = 0
   142  	}
   143  	if hostConfig.Memory > 0 && hostConfig.MemorySwap != -1 && !daemon.SystemConfig().SwapLimit {
   144  		warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.")
   145  		logrus.Warnf("Your kernel does not support swap limit capabilities, memory limited without swap.")
   146  		hostConfig.MemorySwap = -1
   147  	}
   148  	if hostConfig.Memory > 0 && hostConfig.MemorySwap > 0 && hostConfig.MemorySwap < hostConfig.Memory {
   149  		return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage.")
   150  	}
   151  	if hostConfig.Memory == 0 && hostConfig.MemorySwap > 0 {
   152  		return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.")
   153  	}
   154  	if hostConfig.CpuPeriod > 0 && !daemon.SystemConfig().CpuCfsPeriod {
   155  		warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.")
   156  		logrus.Warnf("Your kernel does not support CPU cfs period. Period discarded.")
   157  		hostConfig.CpuPeriod = 0
   158  	}
   159  	if hostConfig.CpuQuota > 0 && !daemon.SystemConfig().CpuCfsQuota {
   160  		warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.")
   161  		logrus.Warnf("Your kernel does not support CPU cfs quota. Quota discarded.")
   162  		hostConfig.CpuQuota = 0
   163  	}
   164  	if hostConfig.BlkioWeight > 0 && (hostConfig.BlkioWeight < 10 || hostConfig.BlkioWeight > 1000) {
   165  		return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000.")
   166  	}
   167  	if hostConfig.OomKillDisable && !daemon.SystemConfig().OomKillDisable {
   168  		hostConfig.OomKillDisable = false
   169  		return warnings, fmt.Errorf("Your kernel does not support oom kill disable.")
   170  	}
   171  	if daemon.SystemConfig().IPv4ForwardingDisabled {
   172  		warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.")
   173  		logrus.Warnf("IPv4 forwarding is disabled. Networking will not work")
   174  	}
   175  	return warnings, nil
   176  }
   177  
   178  // checkConfigOptions checks for mutually incompatible config options
   179  func checkConfigOptions(config *Config) error {
   180  	// Check for mutually incompatible config options
   181  	if config.Bridge.Iface != "" && config.Bridge.IP != "" {
   182  		return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.")
   183  	}
   184  	if !config.Bridge.EnableIPTables && !config.Bridge.InterContainerCommunication {
   185  		return fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.")
   186  	}
   187  	if !config.Bridge.EnableIPTables && config.Bridge.EnableIPMasq {
   188  		config.Bridge.EnableIPMasq = false
   189  	}
   190  	return nil
   191  }
   192  
   193  // checkSystem validates the system is supported and we have sufficient privileges
   194  func checkSystem() error {
   195  	// TODO Windows. Once daemon is running on Windows, move this code back to
   196  	// NewDaemon() in daemon.go, and extend the check to support Windows.
   197  	if runtime.GOOS != "linux" {
   198  		return ErrSystemNotSupported
   199  	}
   200  	if os.Geteuid() != 0 {
   201  		return fmt.Errorf("The Docker daemon needs to be run as root")
   202  	}
   203  	if err := checkKernel(); err != nil {
   204  		return err
   205  	}
   206  	return nil
   207  }
   208  
   209  // configureKernelSecuritySupport configures and validate security support for the kernel
   210  func configureKernelSecuritySupport(config *Config, driverName string) error {
   211  	if config.EnableSelinuxSupport {
   212  		if selinuxEnabled() {
   213  			// As Docker on btrfs and SELinux are incompatible at present, error on both being enabled
   214  			if driverName == "btrfs" {
   215  				return fmt.Errorf("SELinux is not supported with the BTRFS graph driver")
   216  			}
   217  			logrus.Debug("SELinux enabled successfully")
   218  		} else {
   219  			logrus.Warn("Docker could not enable SELinux on the host system")
   220  		}
   221  	} else {
   222  		selinuxSetDisabled()
   223  	}
   224  	return nil
   225  }
   226  
   227  // MigrateIfDownlevel is a wrapper for AUFS migration for downlevel
   228  func migrateIfDownlevel(driver graphdriver.Driver, root string) error {
   229  	return migrateIfAufs(driver, root)
   230  }
   231  
   232  func configureVolumes(config *Config) error {
   233  	volumesDriver, err := local.New(config.Root)
   234  	if err != nil {
   235  		return err
   236  	}
   237  	volumedrivers.Register(volumesDriver, volumesDriver.Name())
   238  	return nil
   239  }
   240  
   241  func configureSysInit(config *Config) (string, error) {
   242  	localCopy := filepath.Join(config.Root, "init", fmt.Sprintf("dockerinit-%s", dockerversion.VERSION))
   243  	sysInitPath := utils.DockerInitPath(localCopy)
   244  	if sysInitPath == "" {
   245  		return "", fmt.Errorf("Could not locate dockerinit: This usually means docker was built incorrectly. See https://docs.docker.com/contributing/devenvironment for official build instructions.")
   246  	}
   247  
   248  	if sysInitPath != localCopy {
   249  		// When we find a suitable dockerinit binary (even if it's our local binary), we copy it into config.Root at localCopy for future use (so that the original can go away without that being a problem, for example during a package upgrade).
   250  		if err := os.Mkdir(filepath.Dir(localCopy), 0700); err != nil && !os.IsExist(err) {
   251  			return "", err
   252  		}
   253  		if _, err := fileutils.CopyFile(sysInitPath, localCopy); err != nil {
   254  			return "", err
   255  		}
   256  		if err := os.Chmod(localCopy, 0700); err != nil {
   257  			return "", err
   258  		}
   259  		sysInitPath = localCopy
   260  	}
   261  	return sysInitPath, nil
   262  }
   263  
   264  func isNetworkDisabled(config *Config) bool {
   265  	return config.Bridge.Iface == disableNetworkBridge
   266  }
   267  
   268  func networkOptions(dconfig *Config) ([]nwconfig.Option, error) {
   269  	options := []nwconfig.Option{}
   270  	if dconfig == nil {
   271  		return options, nil
   272  	}
   273  	if strings.TrimSpace(dconfig.DefaultNetwork) != "" {
   274  		dn := strings.Split(dconfig.DefaultNetwork, ":")
   275  		if len(dn) < 2 {
   276  			return nil, fmt.Errorf("default network daemon config must be of the form NETWORKDRIVER:NETWORKNAME")
   277  		}
   278  		options = append(options, nwconfig.OptionDefaultDriver(dn[0]))
   279  		options = append(options, nwconfig.OptionDefaultNetwork(strings.Join(dn[1:], ":")))
   280  	} else {
   281  		dd := runconfig.DefaultDaemonNetworkMode()
   282  		dn := runconfig.DefaultDaemonNetworkMode().NetworkName()
   283  		options = append(options, nwconfig.OptionDefaultDriver(string(dd)))
   284  		options = append(options, nwconfig.OptionDefaultNetwork(dn))
   285  	}
   286  
   287  	if strings.TrimSpace(dconfig.NetworkKVStore) != "" {
   288  		kv := strings.Split(dconfig.NetworkKVStore, ":")
   289  		if len(kv) < 2 {
   290  			return nil, fmt.Errorf("kv store daemon config must be of the form KV-PROVIDER:KV-URL")
   291  		}
   292  		options = append(options, nwconfig.OptionKVProvider(kv[0]))
   293  		options = append(options, nwconfig.OptionKVProviderURL(strings.Join(kv[1:], ":")))
   294  	}
   295  
   296  	options = append(options, nwconfig.OptionLabels(dconfig.Labels))
   297  	return options, nil
   298  }
   299  
   300  func initNetworkController(config *Config) (libnetwork.NetworkController, error) {
   301  	netOptions, err := networkOptions(config)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	controller, err := libnetwork.New(netOptions...)
   307  	if err != nil {
   308  		return nil, fmt.Errorf("error obtaining controller instance: %v", err)
   309  	}
   310  
   311  	// Initialize default driver "null"
   312  
   313  	if err := controller.ConfigureNetworkDriver("null", options.Generic{}); err != nil {
   314  		return nil, fmt.Errorf("Error initializing null driver: %v", err)
   315  	}
   316  
   317  	// Initialize default network on "null"
   318  	if _, err := controller.NewNetwork("null", "none"); err != nil {
   319  		return nil, fmt.Errorf("Error creating default \"null\" network: %v", err)
   320  	}
   321  
   322  	// Initialize default driver "host"
   323  	if err := controller.ConfigureNetworkDriver("host", options.Generic{}); err != nil {
   324  		return nil, fmt.Errorf("Error initializing host driver: %v", err)
   325  	}
   326  
   327  	// Initialize default network on "host"
   328  	if _, err := controller.NewNetwork("host", "host"); err != nil {
   329  		return nil, fmt.Errorf("Error creating default \"host\" network: %v", err)
   330  	}
   331  
   332  	// Initialize default driver "bridge"
   333  	option := options.Generic{
   334  		"EnableIPForwarding": config.Bridge.EnableIPForward}
   335  
   336  	if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil {
   337  		return nil, fmt.Errorf("Error initializing bridge driver: %v", err)
   338  	}
   339  
   340  	netOption := options.Generic{
   341  		"BridgeName":          config.Bridge.Iface,
   342  		"Mtu":                 config.Mtu,
   343  		"EnableIPTables":      config.Bridge.EnableIPTables,
   344  		"EnableIPMasquerade":  config.Bridge.EnableIPMasq,
   345  		"EnableICC":           config.Bridge.InterContainerCommunication,
   346  		"EnableUserlandProxy": config.Bridge.EnableUserlandProxy,
   347  	}
   348  
   349  	if config.Bridge.IP != "" {
   350  		ip, bipNet, err := net.ParseCIDR(config.Bridge.IP)
   351  		if err != nil {
   352  			return nil, err
   353  		}
   354  
   355  		bipNet.IP = ip
   356  		netOption["AddressIPv4"] = bipNet
   357  	}
   358  
   359  	if config.Bridge.FixedCIDR != "" {
   360  		_, fCIDR, err := net.ParseCIDR(config.Bridge.FixedCIDR)
   361  		if err != nil {
   362  			return nil, err
   363  		}
   364  
   365  		netOption["FixedCIDR"] = fCIDR
   366  	}
   367  
   368  	if config.Bridge.FixedCIDRv6 != "" {
   369  		_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
   370  		if err != nil {
   371  			return nil, err
   372  		}
   373  
   374  		netOption["FixedCIDRv6"] = fCIDRv6
   375  	}
   376  
   377  	if config.Bridge.DefaultGatewayIPv4 != nil {
   378  		netOption["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4
   379  	}
   380  
   381  	if config.Bridge.DefaultGatewayIPv6 != nil {
   382  		netOption["DefaultGatewayIPv6"] = config.Bridge.DefaultGatewayIPv6
   383  	}
   384  
   385  	// --ip processing
   386  	if config.Bridge.DefaultIP != nil {
   387  		netOption["DefaultBindingIP"] = config.Bridge.DefaultIP
   388  	}
   389  
   390  	// Initialize default network on "bridge" with the same name
   391  	_, err = controller.NewNetwork("bridge", "bridge",
   392  		libnetwork.NetworkOptionGeneric(options.Generic{
   393  			netlabel.GenericData: netOption,
   394  			netlabel.EnableIPv6:  config.Bridge.EnableIPv6,
   395  		}))
   396  	if err != nil {
   397  		return nil, fmt.Errorf("Error creating default \"bridge\" network: %v", err)
   398  	}
   399  
   400  	return controller, nil
   401  }
   402  
   403  // setupInitLayer populates a directory with mountpoints suitable
   404  // for bind-mounting dockerinit into the container. The mountpoint is simply an
   405  // empty file at /.dockerinit
   406  //
   407  // This extra layer is used by all containers as the top-most ro layer. It protects
   408  // the container from unwanted side-effects on the rw layer.
   409  func setupInitLayer(initLayer string) error {
   410  	for pth, typ := range map[string]string{
   411  		"/dev/pts":         "dir",
   412  		"/dev/shm":         "dir",
   413  		"/proc":            "dir",
   414  		"/sys":             "dir",
   415  		"/.dockerinit":     "file",
   416  		"/.dockerenv":      "file",
   417  		"/etc/resolv.conf": "file",
   418  		"/etc/hosts":       "file",
   419  		"/etc/hostname":    "file",
   420  		"/dev/console":     "file",
   421  		"/etc/mtab":        "/proc/mounts",
   422  	} {
   423  		parts := strings.Split(pth, "/")
   424  		prev := "/"
   425  		for _, p := range parts[1:] {
   426  			prev = filepath.Join(prev, p)
   427  			syscall.Unlink(filepath.Join(initLayer, prev))
   428  		}
   429  
   430  		if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
   431  			if os.IsNotExist(err) {
   432  				if err := system.MkdirAll(filepath.Join(initLayer, filepath.Dir(pth)), 0755); err != nil {
   433  					return err
   434  				}
   435  				switch typ {
   436  				case "dir":
   437  					if err := system.MkdirAll(filepath.Join(initLayer, pth), 0755); err != nil {
   438  						return err
   439  					}
   440  				case "file":
   441  					f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755)
   442  					if err != nil {
   443  						return err
   444  					}
   445  					f.Close()
   446  				default:
   447  					if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
   448  						return err
   449  					}
   450  				}
   451  			} else {
   452  				return err
   453  			}
   454  		}
   455  	}
   456  
   457  	// Layer is ready to use, if it wasn't before.
   458  	return nil
   459  }
   460  
   461  func (daemon *Daemon) NetworkApiRouter() func(w http.ResponseWriter, req *http.Request) {
   462  	return nwapi.NewHTTPHandler(daemon.netController)
   463  }
   464  
   465  func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error {
   466  
   467  	if hostConfig == nil || hostConfig.Links == nil {
   468  		return nil
   469  	}
   470  
   471  	for _, l := range hostConfig.Links {
   472  		name, alias, err := parsers.ParseLink(l)
   473  		if err != nil {
   474  			return err
   475  		}
   476  		child, err := daemon.Get(name)
   477  		if err != nil {
   478  			//An error from daemon.Get() means this name could not be found
   479  			return fmt.Errorf("Could not get container for %s", name)
   480  		}
   481  		for child.hostConfig.NetworkMode.IsContainer() {
   482  			parts := strings.SplitN(string(child.hostConfig.NetworkMode), ":", 2)
   483  			child, err = daemon.Get(parts[1])
   484  			if err != nil {
   485  				return fmt.Errorf("Could not get container for %s", parts[1])
   486  			}
   487  		}
   488  		if child.hostConfig.NetworkMode.IsHost() {
   489  			return runconfig.ErrConflictHostNetworkAndLinks
   490  		}
   491  		if err := daemon.RegisterLink(container, child, alias); err != nil {
   492  			return err
   493  		}
   494  	}
   495  
   496  	// After we load all the links into the daemon
   497  	// set them to nil on the hostconfig
   498  	hostConfig.Links = nil
   499  	if err := container.WriteHostConfig(); err != nil {
   500  		return err
   501  	}
   502  
   503  	return nil
   504  }