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 }