github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/daemon/daemon_unix.go (about)

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