github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/daemon/daemon_unix.go (about) 1 // +build linux freebsd 2 3 package daemon 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "net" 9 "os" 10 "path/filepath" 11 "runtime" 12 "runtime/debug" 13 "strconv" 14 "strings" 15 "syscall" 16 "time" 17 18 "github.com/Sirupsen/logrus" 19 "github.com/docker/docker/container" 20 "github.com/docker/docker/image" 21 "github.com/docker/docker/layer" 22 "github.com/docker/docker/pkg/idtools" 23 "github.com/docker/docker/pkg/parsers" 24 "github.com/docker/docker/pkg/parsers/kernel" 25 "github.com/docker/docker/pkg/sysinfo" 26 "github.com/docker/docker/reference" 27 "github.com/docker/docker/runconfig" 28 runconfigopts "github.com/docker/docker/runconfig/opts" 29 "github.com/docker/engine-api/types" 30 pblkiodev "github.com/docker/engine-api/types/blkiodev" 31 containertypes "github.com/docker/engine-api/types/container" 32 "github.com/docker/libnetwork" 33 nwconfig "github.com/docker/libnetwork/config" 34 "github.com/docker/libnetwork/drivers/bridge" 35 "github.com/docker/libnetwork/ipamutils" 36 "github.com/docker/libnetwork/netlabel" 37 "github.com/docker/libnetwork/options" 38 lntypes "github.com/docker/libnetwork/types" 39 "github.com/opencontainers/runc/libcontainer/label" 40 "github.com/opencontainers/runc/libcontainer/user" 41 "github.com/opencontainers/specs/specs-go" 42 ) 43 44 const ( 45 // See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269 46 linuxMinCPUShares = 2 47 linuxMaxCPUShares = 262144 48 platformSupported = true 49 // It's not kernel limit, we want this 4M limit to supply a reasonable functional container 50 linuxMinMemory = 4194304 51 // constants for remapped root settings 52 defaultIDSpecifier string = "default" 53 defaultRemappedID string = "dockremap" 54 55 // constant for cgroup drivers 56 cgroupFsDriver = "cgroupfs" 57 cgroupSystemdDriver = "systemd" 58 ) 59 60 func getMemoryResources(config containertypes.Resources) *specs.Memory { 61 memory := specs.Memory{} 62 63 if config.Memory > 0 { 64 limit := uint64(config.Memory) 65 memory.Limit = &limit 66 } 67 68 if config.MemoryReservation > 0 { 69 reservation := uint64(config.MemoryReservation) 70 memory.Reservation = &reservation 71 } 72 73 if config.MemorySwap != 0 { 74 swap := uint64(config.MemorySwap) 75 memory.Swap = &swap 76 } 77 78 if config.MemorySwappiness != nil { 79 swappiness := uint64(*config.MemorySwappiness) 80 memory.Swappiness = &swappiness 81 } 82 83 if config.KernelMemory != 0 { 84 kernelMemory := uint64(config.KernelMemory) 85 memory.Kernel = &kernelMemory 86 } 87 88 return &memory 89 } 90 91 func getCPUResources(config containertypes.Resources) *specs.CPU { 92 cpu := specs.CPU{} 93 94 if config.CPUShares != 0 { 95 shares := uint64(config.CPUShares) 96 cpu.Shares = &shares 97 } 98 99 if config.CpusetCpus != "" { 100 cpuset := config.CpusetCpus 101 cpu.Cpus = &cpuset 102 } 103 104 if config.CpusetMems != "" { 105 cpuset := config.CpusetMems 106 cpu.Mems = &cpuset 107 } 108 109 if config.CPUPeriod != 0 { 110 period := uint64(config.CPUPeriod) 111 cpu.Period = &period 112 } 113 114 if config.CPUQuota != 0 { 115 quota := uint64(config.CPUQuota) 116 cpu.Quota = "a 117 } 118 119 return &cpu 120 } 121 122 func getBlkioWeightDevices(config containertypes.Resources) ([]specs.WeightDevice, error) { 123 var stat syscall.Stat_t 124 var blkioWeightDevices []specs.WeightDevice 125 126 for _, weightDevice := range config.BlkioWeightDevice { 127 if err := syscall.Stat(weightDevice.Path, &stat); err != nil { 128 return nil, err 129 } 130 weight := weightDevice.Weight 131 d := specs.WeightDevice{Weight: &weight} 132 d.Major = int64(stat.Rdev / 256) 133 d.Minor = int64(stat.Rdev % 256) 134 blkioWeightDevices = append(blkioWeightDevices, d) 135 } 136 137 return blkioWeightDevices, nil 138 } 139 140 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error { 141 var ( 142 labelOpts []string 143 err error 144 ) 145 146 for _, opt := range config.SecurityOpt { 147 if opt == "no-new-privileges" { 148 container.NoNewPrivileges = true 149 } else { 150 var con []string 151 if strings.Contains(opt, "=") { 152 con = strings.SplitN(opt, "=", 2) 153 } else if strings.Contains(opt, ":") { 154 con = strings.SplitN(opt, ":", 2) 155 logrus.Warnf("Security options with `:` as a separator are deprecated and will be completely unsupported in 1.13, use `=` instead.") 156 } 157 158 if len(con) != 2 { 159 return fmt.Errorf("Invalid --security-opt 1: %q", opt) 160 } 161 162 switch con[0] { 163 case "label": 164 labelOpts = append(labelOpts, con[1]) 165 case "apparmor": 166 container.AppArmorProfile = con[1] 167 case "seccomp": 168 container.SeccompProfile = con[1] 169 default: 170 return fmt.Errorf("Invalid --security-opt 2: %q", opt) 171 } 172 } 173 } 174 175 container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts) 176 return err 177 } 178 179 func getBlkioReadIOpsDevices(config containertypes.Resources) ([]specs.ThrottleDevice, error) { 180 var blkioReadIOpsDevice []specs.ThrottleDevice 181 var stat syscall.Stat_t 182 183 for _, iopsDevice := range config.BlkioDeviceReadIOps { 184 if err := syscall.Stat(iopsDevice.Path, &stat); err != nil { 185 return nil, err 186 } 187 rate := iopsDevice.Rate 188 d := specs.ThrottleDevice{Rate: &rate} 189 d.Major = int64(stat.Rdev / 256) 190 d.Minor = int64(stat.Rdev % 256) 191 blkioReadIOpsDevice = append(blkioReadIOpsDevice, d) 192 } 193 194 return blkioReadIOpsDevice, nil 195 } 196 197 func getBlkioWriteIOpsDevices(config containertypes.Resources) ([]specs.ThrottleDevice, error) { 198 var blkioWriteIOpsDevice []specs.ThrottleDevice 199 var stat syscall.Stat_t 200 201 for _, iopsDevice := range config.BlkioDeviceWriteIOps { 202 if err := syscall.Stat(iopsDevice.Path, &stat); err != nil { 203 return nil, err 204 } 205 rate := iopsDevice.Rate 206 d := specs.ThrottleDevice{Rate: &rate} 207 d.Major = int64(stat.Rdev / 256) 208 d.Minor = int64(stat.Rdev % 256) 209 blkioWriteIOpsDevice = append(blkioWriteIOpsDevice, d) 210 } 211 212 return blkioWriteIOpsDevice, nil 213 } 214 215 func getBlkioReadBpsDevices(config containertypes.Resources) ([]specs.ThrottleDevice, error) { 216 var blkioReadBpsDevice []specs.ThrottleDevice 217 var stat syscall.Stat_t 218 219 for _, bpsDevice := range config.BlkioDeviceReadBps { 220 if err := syscall.Stat(bpsDevice.Path, &stat); err != nil { 221 return nil, err 222 } 223 rate := bpsDevice.Rate 224 d := specs.ThrottleDevice{Rate: &rate} 225 d.Major = int64(stat.Rdev / 256) 226 d.Minor = int64(stat.Rdev % 256) 227 blkioReadBpsDevice = append(blkioReadBpsDevice, d) 228 } 229 230 return blkioReadBpsDevice, nil 231 } 232 233 func getBlkioWriteBpsDevices(config containertypes.Resources) ([]specs.ThrottleDevice, error) { 234 var blkioWriteBpsDevice []specs.ThrottleDevice 235 var stat syscall.Stat_t 236 237 for _, bpsDevice := range config.BlkioDeviceWriteBps { 238 if err := syscall.Stat(bpsDevice.Path, &stat); err != nil { 239 return nil, err 240 } 241 rate := bpsDevice.Rate 242 d := specs.ThrottleDevice{Rate: &rate} 243 d.Major = int64(stat.Rdev / 256) 244 d.Minor = int64(stat.Rdev % 256) 245 blkioWriteBpsDevice = append(blkioWriteBpsDevice, d) 246 } 247 248 return blkioWriteBpsDevice, nil 249 } 250 251 func checkKernelVersion(k, major, minor int) bool { 252 if v, err := kernel.GetKernelVersion(); err != nil { 253 logrus.Warnf("%s", err) 254 } else { 255 if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: k, Major: major, Minor: minor}) < 0 { 256 return false 257 } 258 } 259 return true 260 } 261 262 func checkKernel() error { 263 // Check for unsupported kernel versions 264 // FIXME: it would be cleaner to not test for specific versions, but rather 265 // test for specific functionalities. 266 // Unfortunately we can't test for the feature "does not cause a kernel panic" 267 // without actually causing a kernel panic, so we need this workaround until 268 // the circumstances of pre-3.10 crashes are clearer. 269 // For details see https://github.com/docker/docker/issues/407 270 if !checkKernelVersion(3, 10, 0) { 271 v, _ := kernel.GetKernelVersion() 272 if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { 273 logrus.Warnf("Your Linux kernel version %s can be unstable running docker. Please upgrade your kernel to 3.10.0.", v.String()) 274 } 275 } 276 return nil 277 } 278 279 // adaptContainerSettings is called during container creation to modify any 280 // settings necessary in the HostConfig structure. 281 func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error { 282 if adjustCPUShares && hostConfig.CPUShares > 0 { 283 // Handle unsupported CPUShares 284 if hostConfig.CPUShares < linuxMinCPUShares { 285 logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, linuxMinCPUShares) 286 hostConfig.CPUShares = linuxMinCPUShares 287 } else if hostConfig.CPUShares > linuxMaxCPUShares { 288 logrus.Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, linuxMaxCPUShares) 289 hostConfig.CPUShares = linuxMaxCPUShares 290 } 291 } 292 if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 { 293 // By default, MemorySwap is set to twice the size of Memory. 294 hostConfig.MemorySwap = hostConfig.Memory * 2 295 } 296 if hostConfig.ShmSize == 0 { 297 hostConfig.ShmSize = container.DefaultSHMSize 298 } 299 var err error 300 if hostConfig.SecurityOpt == nil { 301 hostConfig.SecurityOpt, err = daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode) 302 if err != nil { 303 return err 304 } 305 } 306 if hostConfig.MemorySwappiness == nil { 307 defaultSwappiness := int64(-1) 308 hostConfig.MemorySwappiness = &defaultSwappiness 309 } 310 if hostConfig.OomKillDisable == nil { 311 defaultOomKillDisable := false 312 hostConfig.OomKillDisable = &defaultOomKillDisable 313 } 314 315 return nil 316 } 317 318 func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo, update bool) ([]string, error) { 319 warnings := []string{} 320 321 // memory subsystem checks and adjustments 322 if resources.Memory != 0 && resources.Memory < linuxMinMemory { 323 return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB") 324 } 325 if resources.Memory > 0 && !sysInfo.MemoryLimit { 326 warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") 327 logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") 328 resources.Memory = 0 329 resources.MemorySwap = -1 330 } 331 if resources.Memory > 0 && resources.MemorySwap != -1 && !sysInfo.SwapLimit { 332 warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.") 333 logrus.Warnf("Your kernel does not support swap limit capabilities, memory limited without swap.") 334 resources.MemorySwap = -1 335 } 336 if resources.Memory > 0 && resources.MemorySwap > 0 && resources.MemorySwap < resources.Memory { 337 return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage") 338 } 339 if resources.Memory == 0 && resources.MemorySwap > 0 && !update { 340 return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage") 341 } 342 if resources.MemorySwappiness != nil && *resources.MemorySwappiness != -1 && !sysInfo.MemorySwappiness { 343 warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") 344 logrus.Warnf("Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") 345 resources.MemorySwappiness = nil 346 } 347 if resources.MemorySwappiness != nil { 348 swappiness := *resources.MemorySwappiness 349 if swappiness < -1 || swappiness > 100 { 350 return warnings, fmt.Errorf("Invalid value: %v, valid memory swappiness range is 0-100", swappiness) 351 } 352 } 353 if resources.MemoryReservation > 0 && !sysInfo.MemoryReservation { 354 warnings = append(warnings, "Your kernel does not support memory soft limit capabilities. Limitation discarded.") 355 logrus.Warnf("Your kernel does not support memory soft limit capabilities. Limitation discarded.") 356 resources.MemoryReservation = 0 357 } 358 if resources.MemoryReservation > 0 && resources.MemoryReservation < linuxMinMemory { 359 return warnings, fmt.Errorf("Minimum memory reservation allowed is 4MB") 360 } 361 if resources.Memory > 0 && resources.MemoryReservation > 0 && resources.Memory < resources.MemoryReservation { 362 return warnings, fmt.Errorf("Minimum memory limit should be larger than memory reservation limit, see usage") 363 } 364 if resources.KernelMemory > 0 && !sysInfo.KernelMemory { 365 warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities. Limitation discarded.") 366 logrus.Warnf("Your kernel does not support kernel memory limit capabilities. Limitation discarded.") 367 resources.KernelMemory = 0 368 } 369 if resources.KernelMemory > 0 && resources.KernelMemory < linuxMinMemory { 370 return warnings, fmt.Errorf("Minimum kernel memory limit allowed is 4MB") 371 } 372 if resources.KernelMemory > 0 && !checkKernelVersion(4, 0, 0) { 373 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.") 374 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.") 375 } 376 if resources.OomKillDisable != nil && !sysInfo.OomKillDisable { 377 // only produce warnings if the setting wasn't to *disable* the OOM Kill; no point 378 // warning the caller if they already wanted the feature to be off 379 if *resources.OomKillDisable { 380 warnings = append(warnings, "Your kernel does not support OomKillDisable, OomKillDisable discarded.") 381 logrus.Warnf("Your kernel does not support OomKillDisable, OomKillDisable discarded.") 382 } 383 resources.OomKillDisable = nil 384 } 385 386 if resources.PidsLimit != 0 && !sysInfo.PidsLimit { 387 warnings = append(warnings, "Your kernel does not support pids limit capabilities, pids limit discarded.") 388 logrus.Warnf("Your kernel does not support pids limit capabilities, pids limit discarded.") 389 resources.PidsLimit = 0 390 } 391 392 // cpu subsystem checks and adjustments 393 if resources.CPUShares > 0 && !sysInfo.CPUShares { 394 warnings = append(warnings, "Your kernel does not support CPU shares. Shares discarded.") 395 logrus.Warnf("Your kernel does not support CPU shares. Shares discarded.") 396 resources.CPUShares = 0 397 } 398 if resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod { 399 warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.") 400 logrus.Warnf("Your kernel does not support CPU cfs period. Period discarded.") 401 resources.CPUPeriod = 0 402 } 403 if resources.CPUPeriod > 0 && (resources.CPUPeriod < 1000 || resources.CPUQuota > 1000000) { 404 return warnings, fmt.Errorf("CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)") 405 } 406 if resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota { 407 warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") 408 logrus.Warnf("Your kernel does not support CPU cfs quota. Quota discarded.") 409 resources.CPUQuota = 0 410 } 411 if resources.CPUQuota > 0 && resources.CPUQuota < 1000 { 412 return warnings, fmt.Errorf("CPU cfs quota can not be less than 1ms (i.e. 1000)") 413 } 414 415 // cpuset subsystem checks and adjustments 416 if (resources.CpusetCpus != "" || resources.CpusetMems != "") && !sysInfo.Cpuset { 417 warnings = append(warnings, "Your kernel does not support cpuset. Cpuset discarded.") 418 logrus.Warnf("Your kernel does not support cpuset. Cpuset discarded.") 419 resources.CpusetCpus = "" 420 resources.CpusetMems = "" 421 } 422 cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(resources.CpusetCpus) 423 if err != nil { 424 return warnings, fmt.Errorf("Invalid value %s for cpuset cpus", resources.CpusetCpus) 425 } 426 if !cpusAvailable { 427 return warnings, fmt.Errorf("Requested CPUs are not available - requested %s, available: %s", resources.CpusetCpus, sysInfo.Cpus) 428 } 429 memsAvailable, err := sysInfo.IsCpusetMemsAvailable(resources.CpusetMems) 430 if err != nil { 431 return warnings, fmt.Errorf("Invalid value %s for cpuset mems", resources.CpusetMems) 432 } 433 if !memsAvailable { 434 return warnings, fmt.Errorf("Requested memory nodes are not available - requested %s, available: %s", resources.CpusetMems, sysInfo.Mems) 435 } 436 437 // blkio subsystem checks and adjustments 438 if resources.BlkioWeight > 0 && !sysInfo.BlkioWeight { 439 warnings = append(warnings, "Your kernel does not support Block I/O weight. Weight discarded.") 440 logrus.Warnf("Your kernel does not support Block I/O weight. Weight discarded.") 441 resources.BlkioWeight = 0 442 } 443 if resources.BlkioWeight > 0 && (resources.BlkioWeight < 10 || resources.BlkioWeight > 1000) { 444 return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000") 445 } 446 if len(resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice { 447 warnings = append(warnings, "Your kernel does not support Block I/O weight_device.") 448 logrus.Warnf("Your kernel does not support Block I/O weight_device. Weight-device discarded.") 449 resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{} 450 } 451 if len(resources.BlkioDeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice { 452 warnings = append(warnings, "Your kernel does not support Block read limit in bytes per second.") 453 logrus.Warnf("Your kernel does not support Block I/O read limit in bytes per second. --device-read-bps discarded.") 454 resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{} 455 } 456 if len(resources.BlkioDeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice { 457 warnings = append(warnings, "Your kernel does not support Block write limit in bytes per second.") 458 logrus.Warnf("Your kernel does not support Block I/O write limit in bytes per second. --device-write-bps discarded.") 459 resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{} 460 } 461 if len(resources.BlkioDeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice { 462 warnings = append(warnings, "Your kernel does not support Block read limit in IO per second.") 463 logrus.Warnf("Your kernel does not support Block I/O read limit in IO per second. -device-read-iops discarded.") 464 resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{} 465 } 466 if len(resources.BlkioDeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice { 467 warnings = append(warnings, "Your kernel does not support Block write limit in IO per second.") 468 logrus.Warnf("Your kernel does not support Block I/O write limit in IO per second. --device-write-iops discarded.") 469 resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{} 470 } 471 472 return warnings, nil 473 } 474 475 func (daemon *Daemon) getCgroupDriver() string { 476 cgroupDriver := cgroupFsDriver 477 478 if UsingSystemd(daemon.configStore) { 479 cgroupDriver = cgroupSystemdDriver 480 } 481 return cgroupDriver 482 } 483 484 // getCD gets the raw value of the native.cgroupdriver option, if set. 485 func getCD(config *Config) string { 486 for _, option := range config.ExecOptions { 487 key, val, err := parsers.ParseKeyValueOpt(option) 488 if err != nil || !strings.EqualFold(key, "native.cgroupdriver") { 489 continue 490 } 491 return val 492 } 493 return "" 494 } 495 496 // VerifyCgroupDriver validates native.cgroupdriver 497 func VerifyCgroupDriver(config *Config) error { 498 cd := getCD(config) 499 if cd == "" || cd == cgroupFsDriver || cd == cgroupSystemdDriver { 500 return nil 501 } 502 return fmt.Errorf("native.cgroupdriver option %s not supported", cd) 503 } 504 505 // UsingSystemd returns true if cli option includes native.cgroupdriver=systemd 506 func UsingSystemd(config *Config) bool { 507 return getCD(config) == cgroupSystemdDriver 508 } 509 510 // verifyPlatformContainerSettings performs platform-specific validation of the 511 // hostconfig and config structures. 512 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { 513 warnings := []string{} 514 sysInfo := sysinfo.New(true) 515 516 warnings, err := daemon.verifyExperimentalContainerSettings(hostConfig, config) 517 if err != nil { 518 return warnings, err 519 } 520 521 w, err := verifyContainerResources(&hostConfig.Resources, sysInfo, update) 522 if err != nil { 523 return warnings, err 524 } 525 warnings = append(warnings, w...) 526 527 if hostConfig.ShmSize < 0 { 528 return warnings, fmt.Errorf("SHM size must be greater than 0") 529 } 530 531 if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 { 532 return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj) 533 } 534 if sysInfo.IPv4ForwardingDisabled { 535 warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") 536 logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") 537 } 538 // check for various conflicting options with user namespaces 539 if daemon.configStore.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() { 540 if hostConfig.Privileged { 541 return warnings, fmt.Errorf("Privileged mode is incompatible with user namespaces") 542 } 543 if hostConfig.NetworkMode.IsHost() { 544 return warnings, fmt.Errorf("Cannot share the host's network namespace when user namespaces are enabled") 545 } 546 if hostConfig.PidMode.IsHost() { 547 return warnings, fmt.Errorf("Cannot share the host PID namespace when user namespaces are enabled") 548 } 549 if hostConfig.ReadonlyRootfs { 550 return warnings, fmt.Errorf("Cannot use the --read-only option when user namespaces are enabled") 551 } 552 } 553 if hostConfig.CgroupParent != "" && UsingSystemd(daemon.configStore) { 554 // CgroupParent for systemd cgroup should be named as "xxx.slice" 555 if len(hostConfig.CgroupParent) <= 6 || !strings.HasSuffix(hostConfig.CgroupParent, ".slice") { 556 return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"") 557 } 558 } 559 return warnings, nil 560 } 561 562 // verifyDaemonSettings performs validation of daemon config struct 563 func verifyDaemonSettings(config *Config) error { 564 // Check for mutually incompatible config options 565 if config.bridgeConfig.Iface != "" && config.bridgeConfig.IP != "" { 566 return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one") 567 } 568 if !config.bridgeConfig.EnableIPTables && !config.bridgeConfig.InterContainerCommunication { 569 return fmt.Errorf("You specified --iptables=false with --icc=false. ICC=false uses iptables to function. Please set --icc or --iptables to true") 570 } 571 if !config.bridgeConfig.EnableIPTables && config.bridgeConfig.EnableIPMasq { 572 config.bridgeConfig.EnableIPMasq = false 573 } 574 if err := VerifyCgroupDriver(config); err != nil { 575 return err 576 } 577 if config.CgroupParent != "" && UsingSystemd(config) { 578 if len(config.CgroupParent) <= 6 || !strings.HasSuffix(config.CgroupParent, ".slice") { 579 return fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"") 580 } 581 } 582 return nil 583 } 584 585 // checkSystem validates platform-specific requirements 586 func checkSystem() error { 587 if os.Geteuid() != 0 { 588 return fmt.Errorf("The Docker daemon needs to be run as root") 589 } 590 return checkKernel() 591 } 592 593 // configureMaxThreads sets the Go runtime max threads threshold 594 // which is 90% of the kernel setting from /proc/sys/kernel/threads-max 595 func configureMaxThreads(config *Config) error { 596 mt, err := ioutil.ReadFile("/proc/sys/kernel/threads-max") 597 if err != nil { 598 return err 599 } 600 mtint, err := strconv.Atoi(strings.TrimSpace(string(mt))) 601 if err != nil { 602 return err 603 } 604 maxThreads := (mtint / 100) * 90 605 debug.SetMaxThreads(maxThreads) 606 logrus.Debugf("Golang's threads limit set to %d", maxThreads) 607 return nil 608 } 609 610 // configureKernelSecuritySupport configures and validates security support for the kernel 611 func configureKernelSecuritySupport(config *Config, driverName string) error { 612 if config.EnableSelinuxSupport { 613 if selinuxEnabled() { 614 // As Docker on overlayFS and SELinux are incompatible at present, error on overlayfs being enabled 615 if driverName == "overlay" { 616 return fmt.Errorf("SELinux is not supported with the %s graph driver", driverName) 617 } 618 logrus.Debug("SELinux enabled successfully") 619 } else { 620 logrus.Warn("Docker could not enable SELinux on the host system") 621 } 622 } else { 623 selinuxSetDisabled() 624 } 625 return nil 626 } 627 628 func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) { 629 netOptions, err := daemon.networkOptions(config) 630 if err != nil { 631 return nil, err 632 } 633 634 controller, err := libnetwork.New(netOptions...) 635 if err != nil { 636 return nil, fmt.Errorf("error obtaining controller instance: %v", err) 637 } 638 639 // Initialize default network on "null" 640 if _, err := controller.NewNetwork("null", "none", libnetwork.NetworkOptionPersist(false)); err != nil { 641 return nil, fmt.Errorf("Error creating default \"null\" network: %v", err) 642 } 643 644 // Initialize default network on "host" 645 if _, err := controller.NewNetwork("host", "host", libnetwork.NetworkOptionPersist(false)); err != nil { 646 return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) 647 } 648 649 if !config.DisableBridge { 650 // Initialize default driver "bridge" 651 if err := initBridgeDriver(controller, config); err != nil { 652 return nil, err 653 } 654 } 655 656 return controller, nil 657 } 658 659 func driverOptions(config *Config) []nwconfig.Option { 660 bridgeConfig := options.Generic{ 661 "EnableIPForwarding": config.bridgeConfig.EnableIPForward, 662 "EnableIPTables": config.bridgeConfig.EnableIPTables, 663 "EnableUserlandProxy": config.bridgeConfig.EnableUserlandProxy} 664 bridgeOption := options.Generic{netlabel.GenericData: bridgeConfig} 665 666 dOptions := []nwconfig.Option{} 667 dOptions = append(dOptions, nwconfig.OptionDriverConfig("bridge", bridgeOption)) 668 return dOptions 669 } 670 671 func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error { 672 if n, err := controller.NetworkByName("bridge"); err == nil { 673 if err = n.Delete(); err != nil { 674 return fmt.Errorf("could not delete the default bridge network: %v", err) 675 } 676 } 677 678 bridgeName := bridge.DefaultBridgeName 679 if config.bridgeConfig.Iface != "" { 680 bridgeName = config.bridgeConfig.Iface 681 } 682 netOption := map[string]string{ 683 bridge.BridgeName: bridgeName, 684 bridge.DefaultBridge: strconv.FormatBool(true), 685 netlabel.DriverMTU: strconv.Itoa(config.Mtu), 686 bridge.EnableIPMasquerade: strconv.FormatBool(config.bridgeConfig.EnableIPMasq), 687 bridge.EnableICC: strconv.FormatBool(config.bridgeConfig.InterContainerCommunication), 688 } 689 690 // --ip processing 691 if config.bridgeConfig.DefaultIP != nil { 692 netOption[bridge.DefaultBindingIP] = config.bridgeConfig.DefaultIP.String() 693 } 694 695 var ( 696 ipamV4Conf *libnetwork.IpamConf 697 ipamV6Conf *libnetwork.IpamConf 698 ) 699 700 ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} 701 702 nw, nw6List, err := ipamutils.ElectInterfaceAddresses(bridgeName) 703 if err == nil { 704 ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String() 705 hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask) 706 if hip.IsGlobalUnicast() { 707 ipamV4Conf.Gateway = nw.IP.String() 708 } 709 } 710 711 if config.bridgeConfig.IP != "" { 712 ipamV4Conf.PreferredPool = config.bridgeConfig.IP 713 ip, _, err := net.ParseCIDR(config.bridgeConfig.IP) 714 if err != nil { 715 return err 716 } 717 ipamV4Conf.Gateway = ip.String() 718 } else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" { 719 logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool) 720 } 721 722 if config.bridgeConfig.FixedCIDR != "" { 723 _, fCIDR, err := net.ParseCIDR(config.bridgeConfig.FixedCIDR) 724 if err != nil { 725 return err 726 } 727 728 ipamV4Conf.SubPool = fCIDR.String() 729 } 730 731 if config.bridgeConfig.DefaultGatewayIPv4 != nil { 732 ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.bridgeConfig.DefaultGatewayIPv4.String() 733 } 734 735 var deferIPv6Alloc bool 736 if config.bridgeConfig.FixedCIDRv6 != "" { 737 _, fCIDRv6, err := net.ParseCIDR(config.bridgeConfig.FixedCIDRv6) 738 if err != nil { 739 return err 740 } 741 742 // In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has 743 // at least 48 host bits, we need to guarantee the current behavior where the containers' 744 // IPv6 addresses will be constructed based on the containers' interface MAC address. 745 // We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints 746 // on this network until after the driver has created the endpoint and returned the 747 // constructed address. Libnetwork will then reserve this address with the ipam driver. 748 ones, _ := fCIDRv6.Mask.Size() 749 deferIPv6Alloc = ones <= 80 750 751 if ipamV6Conf == nil { 752 ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} 753 } 754 ipamV6Conf.PreferredPool = fCIDRv6.String() 755 756 // In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6 757 // address belongs to the same network, we need to inform libnetwork about it, so 758 // that it can be reserved with IPAM and it will not be given away to somebody else 759 for _, nw6 := range nw6List { 760 if fCIDRv6.Contains(nw6.IP) { 761 ipamV6Conf.Gateway = nw6.IP.String() 762 break 763 } 764 } 765 } 766 767 if config.bridgeConfig.DefaultGatewayIPv6 != nil { 768 if ipamV6Conf == nil { 769 ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} 770 } 771 ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = config.bridgeConfig.DefaultGatewayIPv6.String() 772 } 773 774 v4Conf := []*libnetwork.IpamConf{ipamV4Conf} 775 v6Conf := []*libnetwork.IpamConf{} 776 if ipamV6Conf != nil { 777 v6Conf = append(v6Conf, ipamV6Conf) 778 } 779 // Initialize default network on "bridge" with the same name 780 _, err = controller.NewNetwork("bridge", "bridge", 781 libnetwork.NetworkOptionEnableIPv6(config.bridgeConfig.EnableIPv6), 782 libnetwork.NetworkOptionDriverOpts(netOption), 783 libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), 784 libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc)) 785 if err != nil { 786 return fmt.Errorf("Error creating default \"bridge\" network: %v", err) 787 } 788 return nil 789 } 790 791 // setupInitLayer populates a directory with mountpoints suitable 792 // for bind-mounting things into the container. 793 // 794 // This extra layer is used by all containers as the top-most ro layer. It protects 795 // the container from unwanted side-effects on the rw layer. 796 func setupInitLayer(initLayer string, rootUID, rootGID int) error { 797 for pth, typ := range map[string]string{ 798 "/dev/pts": "dir", 799 "/dev/shm": "dir", 800 "/proc": "dir", 801 "/sys": "dir", 802 "/.dockerenv": "file", 803 "/etc/resolv.conf": "file", 804 "/etc/hosts": "file", 805 "/etc/hostname": "file", 806 "/dev/console": "file", 807 "/etc/mtab": "/proc/mounts", 808 } { 809 parts := strings.Split(pth, "/") 810 prev := "/" 811 for _, p := range parts[1:] { 812 prev = filepath.Join(prev, p) 813 syscall.Unlink(filepath.Join(initLayer, prev)) 814 } 815 816 if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil { 817 if os.IsNotExist(err) { 818 if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootUID, rootGID); err != nil { 819 return err 820 } 821 switch typ { 822 case "dir": 823 if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, pth), 0755, rootUID, rootGID); err != nil { 824 return err 825 } 826 case "file": 827 f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755) 828 if err != nil { 829 return err 830 } 831 f.Chown(rootUID, rootGID) 832 f.Close() 833 default: 834 if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil { 835 return err 836 } 837 } 838 } else { 839 return err 840 } 841 } 842 } 843 844 // Layer is ready to use, if it wasn't before. 845 return nil 846 } 847 848 // Parse the remapped root (user namespace) option, which can be one of: 849 // username - valid username from /etc/passwd 850 // username:groupname - valid username; valid groupname from /etc/group 851 // uid - 32-bit unsigned int valid Linux UID value 852 // uid:gid - uid value; 32-bit unsigned int Linux GID value 853 // 854 // If no groupname is specified, and a username is specified, an attempt 855 // will be made to lookup a gid for that username as a groupname 856 // 857 // If names are used, they are verified to exist in passwd/group 858 func parseRemappedRoot(usergrp string) (string, string, error) { 859 860 var ( 861 userID, groupID int 862 username, groupname string 863 ) 864 865 idparts := strings.Split(usergrp, ":") 866 if len(idparts) > 2 { 867 return "", "", fmt.Errorf("Invalid user/group specification in --userns-remap: %q", usergrp) 868 } 869 870 if uid, err := strconv.ParseInt(idparts[0], 10, 32); err == nil { 871 // must be a uid; take it as valid 872 userID = int(uid) 873 luser, err := user.LookupUid(userID) 874 if err != nil { 875 return "", "", fmt.Errorf("Uid %d has no entry in /etc/passwd: %v", userID, err) 876 } 877 username = luser.Name 878 if len(idparts) == 1 { 879 // if the uid was numeric and no gid was specified, take the uid as the gid 880 groupID = userID 881 lgrp, err := user.LookupGid(groupID) 882 if err != nil { 883 return "", "", fmt.Errorf("Gid %d has no entry in /etc/group: %v", groupID, err) 884 } 885 groupname = lgrp.Name 886 } 887 } else { 888 lookupName := idparts[0] 889 // special case: if the user specified "default", they want Docker to create or 890 // use (after creation) the "dockremap" user/group for root remapping 891 if lookupName == defaultIDSpecifier { 892 lookupName = defaultRemappedID 893 } 894 luser, err := user.LookupUser(lookupName) 895 if err != nil && idparts[0] != defaultIDSpecifier { 896 // error if the name requested isn't the special "dockremap" ID 897 return "", "", fmt.Errorf("Error during uid lookup for %q: %v", lookupName, err) 898 } else if err != nil { 899 // special case-- if the username == "default", then we have been asked 900 // to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid} 901 // ranges will be used for the user and group mappings in user namespaced containers 902 _, _, err := idtools.AddNamespaceRangesUser(defaultRemappedID) 903 if err == nil { 904 return defaultRemappedID, defaultRemappedID, nil 905 } 906 return "", "", fmt.Errorf("Error during %q user creation: %v", defaultRemappedID, err) 907 } 908 username = luser.Name 909 if len(idparts) == 1 { 910 // we only have a string username, and no group specified; look up gid from username as group 911 group, err := user.LookupGroup(lookupName) 912 if err != nil { 913 return "", "", fmt.Errorf("Error during gid lookup for %q: %v", lookupName, err) 914 } 915 groupID = group.Gid 916 groupname = group.Name 917 } 918 } 919 920 if len(idparts) == 2 { 921 // groupname or gid is separately specified and must be resolved 922 // to an unsigned 32-bit gid 923 if gid, err := strconv.ParseInt(idparts[1], 10, 32); err == nil { 924 // must be a gid, take it as valid 925 groupID = int(gid) 926 lgrp, err := user.LookupGid(groupID) 927 if err != nil { 928 return "", "", fmt.Errorf("Gid %d has no entry in /etc/passwd: %v", groupID, err) 929 } 930 groupname = lgrp.Name 931 } else { 932 // not a number; attempt a lookup 933 if _, err := user.LookupGroup(idparts[1]); err != nil { 934 return "", "", fmt.Errorf("Error during groupname lookup for %q: %v", idparts[1], err) 935 } 936 groupname = idparts[1] 937 } 938 } 939 return username, groupname, nil 940 } 941 942 func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { 943 if runtime.GOOS != "linux" && config.RemappedRoot != "" { 944 return nil, nil, fmt.Errorf("User namespaces are only supported on Linux") 945 } 946 947 // if the daemon was started with remapped root option, parse 948 // the config option to the int uid,gid values 949 var ( 950 uidMaps, gidMaps []idtools.IDMap 951 ) 952 if config.RemappedRoot != "" { 953 username, groupname, err := parseRemappedRoot(config.RemappedRoot) 954 if err != nil { 955 return nil, nil, err 956 } 957 if username == "root" { 958 // Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op 959 // effectively 960 logrus.Warnf("User namespaces: root cannot be remapped with itself; user namespaces are OFF") 961 return uidMaps, gidMaps, nil 962 } 963 logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname) 964 // update remapped root setting now that we have resolved them to actual names 965 config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname) 966 967 uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname) 968 if err != nil { 969 return nil, nil, fmt.Errorf("Can't create ID mappings: %v", err) 970 } 971 } 972 return uidMaps, gidMaps, nil 973 } 974 975 func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error { 976 config.Root = rootDir 977 // the docker root metadata directory needs to have execute permissions for all users (g+x,o+x) 978 // so that syscalls executing as non-root, operating on subdirectories of the graph root 979 // (e.g. mounted layers of a container) can traverse this path. 980 // The user namespace support will create subdirectories for the remapped root host uid:gid 981 // pair owned by that same uid:gid pair for proper write access to those needed metadata and 982 // layer content subtrees. 983 if _, err := os.Stat(rootDir); err == nil { 984 // root current exists; verify the access bits are correct by setting them 985 if err = os.Chmod(rootDir, 0711); err != nil { 986 return err 987 } 988 } else if os.IsNotExist(err) { 989 // no root exists yet, create it 0711 with root:root ownership 990 if err := os.MkdirAll(rootDir, 0711); err != nil { 991 return err 992 } 993 } 994 995 // if user namespaces are enabled we will create a subtree underneath the specified root 996 // with any/all specified remapped root uid/gid options on the daemon creating 997 // a new subdirectory with ownership set to the remapped uid/gid (so as to allow 998 // `chdir()` to work for containers namespaced to that uid/gid) 999 if config.RemappedRoot != "" { 1000 config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootUID, rootGID)) 1001 logrus.Debugf("Creating user namespaced daemon root: %s", config.Root) 1002 // Create the root directory if it doesn't exist 1003 if err := idtools.MkdirAllAs(config.Root, 0700, rootUID, rootGID); err != nil { 1004 return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err) 1005 } 1006 } 1007 return nil 1008 } 1009 1010 // registerLinks writes the links to a file. 1011 func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error { 1012 if hostConfig == nil || hostConfig.NetworkMode.IsUserDefined() { 1013 return nil 1014 } 1015 1016 for _, l := range hostConfig.Links { 1017 name, alias, err := runconfigopts.ParseLink(l) 1018 if err != nil { 1019 return err 1020 } 1021 child, err := daemon.GetContainer(name) 1022 if err != nil { 1023 //An error from daemon.GetContainer() means this name could not be found 1024 return fmt.Errorf("Could not get container for %s", name) 1025 } 1026 for child.HostConfig.NetworkMode.IsContainer() { 1027 parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2) 1028 child, err = daemon.GetContainer(parts[1]) 1029 if err != nil { 1030 return fmt.Errorf("Could not get container for %s", parts[1]) 1031 } 1032 } 1033 if child.HostConfig.NetworkMode.IsHost() { 1034 return runconfig.ErrConflictHostNetworkAndLinks 1035 } 1036 if err := daemon.registerLink(container, child, alias); err != nil { 1037 return err 1038 } 1039 } 1040 1041 // After we load all the links into the daemon 1042 // set them to nil on the hostconfig 1043 return container.WriteHostConfig() 1044 } 1045 1046 // conditionalMountOnStart is a platform specific helper function during the 1047 // container start to call mount. 1048 func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { 1049 return daemon.Mount(container) 1050 } 1051 1052 // conditionalUnmountOnCleanup is a platform specific helper function called 1053 // during the cleanup of a container to unmount. 1054 func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error { 1055 return daemon.Unmount(container) 1056 } 1057 1058 func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error { 1059 // Unix has no custom images to register 1060 return nil 1061 } 1062 1063 func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { 1064 if !c.IsRunning() { 1065 return nil, errNotRunning{c.ID} 1066 } 1067 stats, err := daemon.containerd.Stats(c.ID) 1068 if err != nil { 1069 return nil, err 1070 } 1071 s := &types.StatsJSON{} 1072 cgs := stats.CgroupStats 1073 if cgs != nil { 1074 s.BlkioStats = types.BlkioStats{ 1075 IoServiceBytesRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceBytesRecursive), 1076 IoServicedRecursive: copyBlkioEntry(cgs.BlkioStats.IoServicedRecursive), 1077 IoQueuedRecursive: copyBlkioEntry(cgs.BlkioStats.IoQueuedRecursive), 1078 IoServiceTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceTimeRecursive), 1079 IoWaitTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoWaitTimeRecursive), 1080 IoMergedRecursive: copyBlkioEntry(cgs.BlkioStats.IoMergedRecursive), 1081 IoTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoTimeRecursive), 1082 SectorsRecursive: copyBlkioEntry(cgs.BlkioStats.SectorsRecursive), 1083 } 1084 cpu := cgs.CpuStats 1085 s.CPUStats = types.CPUStats{ 1086 CPUUsage: types.CPUUsage{ 1087 TotalUsage: cpu.CpuUsage.TotalUsage, 1088 PercpuUsage: cpu.CpuUsage.PercpuUsage, 1089 UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode, 1090 UsageInUsermode: cpu.CpuUsage.UsageInUsermode, 1091 }, 1092 ThrottlingData: types.ThrottlingData{ 1093 Periods: cpu.ThrottlingData.Periods, 1094 ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods, 1095 ThrottledTime: cpu.ThrottlingData.ThrottledTime, 1096 }, 1097 } 1098 mem := cgs.MemoryStats.Usage 1099 s.MemoryStats = types.MemoryStats{ 1100 Usage: mem.Usage, 1101 MaxUsage: mem.MaxUsage, 1102 Stats: cgs.MemoryStats.Stats, 1103 Failcnt: mem.Failcnt, 1104 Limit: mem.Limit, 1105 } 1106 // if the container does not set memory limit, use the machineMemory 1107 if mem.Limit > daemon.statsCollector.machineMemory && daemon.statsCollector.machineMemory > 0 { 1108 s.MemoryStats.Limit = daemon.statsCollector.machineMemory 1109 } 1110 if cgs.PidsStats != nil { 1111 s.PidsStats = types.PidsStats{ 1112 Current: cgs.PidsStats.Current, 1113 } 1114 } 1115 } 1116 s.Read = time.Unix(int64(stats.Timestamp), 0) 1117 return s, nil 1118 } 1119 1120 // setDefaultIsolation determines the default isolation mode for the 1121 // daemon to run in. This is only applicable on Windows 1122 func (daemon *Daemon) setDefaultIsolation() error { 1123 return nil 1124 } 1125 1126 func rootFSToAPIType(rootfs *image.RootFS) types.RootFS { 1127 var layers []string 1128 for _, l := range rootfs.DiffIDs { 1129 layers = append(layers, l.String()) 1130 } 1131 return types.RootFS{ 1132 Type: rootfs.Type, 1133 Layers: layers, 1134 } 1135 }