1 // +build linux freebsd
20 "github.com/Sirupsen/logrus"
21 "github.com/docker/docker/api/types"
22 "github.com/docker/docker/api/types/blkiodev"
23 pblkiodev "github.com/docker/docker/api/types/blkiodev"
24 containertypes "github.com/docker/docker/api/types/container"
25 "github.com/docker/docker/container"
26 "github.com/docker/docker/daemon/config"
27 "github.com/docker/docker/image"
28 "github.com/docker/docker/opts"
29 "github.com/docker/docker/pkg/idtools"
30 "github.com/docker/docker/pkg/parsers"
31 "github.com/docker/docker/pkg/parsers/kernel"
32 "github.com/docker/docker/pkg/sysinfo"
33 "github.com/docker/docker/runconfig"
34 "github.com/docker/docker/volume"
35 "github.com/docker/libnetwork"
36 nwconfig "github.com/docker/libnetwork/config"
37 "github.com/docker/libnetwork/drivers/bridge"
38 "github.com/docker/libnetwork/netlabel"
39 "github.com/docker/libnetwork/netutils"
40 "github.com/docker/libnetwork/options"
41 lntypes "github.com/docker/libnetwork/types"
42 "github.com/golang/protobuf/ptypes"
43 "github.com/opencontainers/runc/libcontainer/cgroups"
44 rsystem "github.com/opencontainers/runc/libcontainer/system"
45 specs "github.com/opencontainers/runtime-spec/specs-go"
46 "github.com/opencontainers/selinux/go-selinux/label"
47 "github.com/pkg/errors"
48 "github.com/vishvananda/netlink"
52 // See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269
54 linuxMaxCPUShares = 262144
55 platformSupported = true
56 // It's not kernel limit, we want this 4M limit to supply a reasonable functional container
57 linuxMinMemory = 4194304
58 // constants for remapped root settings
59 defaultIDSpecifier string = "default"
60 defaultRemappedID string = "dockremap"
62 // constant for cgroup drivers
63 cgroupFsDriver = "cgroupfs"
64 cgroupSystemdDriver = "systemd"
67 func getMemoryResources(config containertypes.Resources) *specs.LinuxMemory {
68 memory := specs.LinuxMemory{}
70 if config.Memory > 0 {
71 limit := uint64(config.Memory)
75 if config.MemoryReservation > 0 {
76 reservation := uint64(config.MemoryReservation)
77 memory.Reservation = &reservation
80 if config.MemorySwap > 0 {
81 swap := uint64(config.MemorySwap)
85 if config.MemorySwappiness != nil {
86 swappiness := uint64(*config.MemorySwappiness)
87 memory.Swappiness = &swappiness
90 if config.KernelMemory != 0 {
91 kernelMemory := uint64(config.KernelMemory)
92 memory.Kernel = &kernelMemory
98 func getCPUResources(config containertypes.Resources) (*specs.LinuxCPU, error) {
99 cpu := specs.LinuxCPU{}
101 if config.CPUShares < 0 {
102 return nil, fmt.Errorf("shares: invalid argument")
104 if config.CPUShares >= 0 {
105 shares := uint64(config.CPUShares)
109 if config.CpusetCpus != "" {
110 cpu.Cpus = config.CpusetCpus
113 if config.CpusetMems != "" {
114 cpu.Mems = config.CpusetMems
117 if config.NanoCPUs > 0 {
118 // https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
119 period := uint64(100 * time.Millisecond / time.Microsecond)
120 quota := config.NanoCPUs * int64(period) / 1e9
125 if config.CPUPeriod != 0 {
126 period := uint64(config.CPUPeriod)
130 if config.CPUQuota != 0 {
135 if config.CPURealtimePeriod != 0 {
136 period := uint64(config.CPURealtimePeriod)
137 cpu.RealtimePeriod = &period
140 if config.CPURealtimeRuntime != 0 {
141 c := config.CPURealtimeRuntime
142 cpu.RealtimeRuntime = &c
148 func getBlkioWeightDevices(config containertypes.Resources) ([]specs.LinuxWeightDevice, error) {
149 var stat syscall.Stat_t
150 var blkioWeightDevices []specs.LinuxWeightDevice
152 for _, weightDevice := range config.BlkioWeightDevice {
153 if err := syscall.Stat(weightDevice.Path, &stat); err != nil {
156 weight := weightDevice.Weight
157 d := specs.LinuxWeightDevice{Weight: &weight}
158 d.Major = int64(stat.Rdev / 256)
159 d.Minor = int64(stat.Rdev % 256)
160 blkioWeightDevices = append(blkioWeightDevices, d)
163 return blkioWeightDevices, nil
166 func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
167 container.NoNewPrivileges = daemon.configStore.NoNewPrivileges
168 return parseSecurityOpt(container, hostConfig)
171 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
177 for _, opt := range config.SecurityOpt {
178 if opt == "no-new-privileges" {
179 container.NoNewPrivileges = true
182 if opt == "disable" {
183 labelOpts = append(labelOpts, "disable")
188 if strings.Contains(opt, "=") {
189 con = strings.SplitN(opt, "=", 2)
190 } else if strings.Contains(opt, ":") {
191 con = strings.SplitN(opt, ":", 2)
192 logrus.Warn("Security options with `:` as a separator are deprecated and will be completely unsupported in 17.04, use `=` instead.")
195 return fmt.Errorf("invalid --security-opt 1: %q", opt)
200 labelOpts = append(labelOpts, con[1])
202 container.AppArmorProfile = con[1]
204 container.SeccompProfile = con[1]
205 case "no-new-privileges":
206 noNewPrivileges, err := strconv.ParseBool(con[1])
208 return fmt.Errorf("invalid --security-opt 2: %q", opt)
210 container.NoNewPrivileges = noNewPrivileges
212 return fmt.Errorf("invalid --security-opt 2: %q", opt)
216 container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts)
220 func getBlkioThrottleDevices(devs []*blkiodev.ThrottleDevice) ([]specs.LinuxThrottleDevice, error) {
221 var throttleDevices []specs.LinuxThrottleDevice
222 var stat syscall.Stat_t
224 for _, d := range devs {
225 if err := syscall.Stat(d.Path, &stat); err != nil {
228 d := specs.LinuxThrottleDevice{Rate: d.Rate}
229 d.Major = int64(stat.Rdev / 256)
230 d.Minor = int64(stat.Rdev % 256)
231 throttleDevices = append(throttleDevices, d)
234 return throttleDevices, nil
237 func checkKernel() error {
238 // Check for unsupported kernel versions
239 // FIXME: it would be cleaner to not test for specific versions, but rather
240 // test for specific functionalities.
241 // Unfortunately we can't test for the feature "does not cause a kernel panic"
242 // without actually causing a kernel panic, so we need this workaround until
243 // the circumstances of pre-3.10 crashes are clearer.
244 // For details see https://github.com/docker/docker/issues/407
245 // Docker 1.11 and above doesn't actually run on kernels older than 3.4,
246 // due to containerd-shim usage of PR_SET_CHILD_SUBREAPER (introduced in 3.4).
247 if !kernel.CheckKernelVersion(3, 10, 0) {
248 v, _ := kernel.GetKernelVersion()
249 if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" {
250 logrus.Fatalf("Your Linux kernel version %s is not supported for running docker. Please upgrade your kernel to 3.10.0 or newer.", v.String())
256 // adaptContainerSettings is called during container creation to modify any
257 // settings necessary in the HostConfig structure.
258 func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
259 if adjustCPUShares && hostConfig.CPUShares > 0 {
260 // Handle unsupported CPUShares
261 if hostConfig.CPUShares < linuxMinCPUShares {
262 logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, linuxMinCPUShares)
263 hostConfig.CPUShares = linuxMinCPUShares
264 } else if hostConfig.CPUShares > linuxMaxCPUShares {
265 logrus.Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, linuxMaxCPUShares)
266 hostConfig.CPUShares = linuxMaxCPUShares
269 if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 {
270 // By default, MemorySwap is set to twice the size of Memory.
271 hostConfig.MemorySwap = hostConfig.Memory * 2
273 if hostConfig.ShmSize == 0 {
274 hostConfig.ShmSize = config.DefaultShmSize
275 if daemon.configStore != nil {
276 hostConfig.ShmSize = int64(daemon.configStore.ShmSize)
280 opts, err := daemon.generateSecurityOpt(hostConfig)
284 hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, opts...)
285 if hostConfig.MemorySwappiness == nil {
286 defaultSwappiness := int64(-1)
287 hostConfig.MemorySwappiness = &defaultSwappiness
289 if hostConfig.OomKillDisable == nil {
290 defaultOomKillDisable := false
291 hostConfig.OomKillDisable = &defaultOomKillDisable
297 func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo, update bool) ([]string, error) {
298 warnings := []string{}
300 // memory subsystem checks and adjustments
301 if resources.Memory != 0 && resources.Memory < linuxMinMemory {
302 return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB")
304 if resources.Memory > 0 && !sysInfo.MemoryLimit {
305 warnings = append(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
306 logrus.Warn("Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
308 resources.MemorySwap = -1
310 if resources.Memory > 0 && resources.MemorySwap != -1 && !sysInfo.SwapLimit {
311 warnings = append(warnings, "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.")
312 logrus.Warn("Your kernel does not support swap limit capabilities,or the cgroup is not mounted. Memory limited without swap.")
313 resources.MemorySwap = -1
315 if resources.Memory > 0 && resources.MemorySwap > 0 && resources.MemorySwap < resources.Memory {
316 return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage")
318 if resources.Memory == 0 && resources.MemorySwap > 0 && !update {
319 return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage")
321 if resources.MemorySwappiness != nil && *resources.MemorySwappiness != -1 && !sysInfo.MemorySwappiness {
322 warnings = append(warnings, "Your kernel does not support memory swappiness capabilities or the cgroup is not mounted. Memory swappiness discarded.")
323 logrus.Warn("Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.")
324 resources.MemorySwappiness = nil
326 if resources.MemorySwappiness != nil {
327 swappiness := *resources.MemorySwappiness
328 if swappiness < -1 || swappiness > 100 {
329 return warnings, fmt.Errorf("Invalid value: %v, valid memory swappiness range is 0-100", swappiness)
332 if resources.MemoryReservation > 0 && !sysInfo.MemoryReservation {
333 warnings = append(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.")
334 logrus.Warn("Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.")
335 resources.MemoryReservation = 0
337 if resources.MemoryReservation > 0 && resources.MemoryReservation < linuxMinMemory {
338 return warnings, fmt.Errorf("Minimum memory reservation allowed is 4MB")
340 if resources.Memory > 0 && resources.MemoryReservation > 0 && resources.Memory < resources.MemoryReservation {
341 return warnings, fmt.Errorf("Minimum memory limit can not be less than memory reservation limit, see usage")
343 if resources.KernelMemory > 0 && !sysInfo.KernelMemory {
344 warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
345 logrus.Warn("Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
346 resources.KernelMemory = 0
348 if resources.KernelMemory > 0 && resources.KernelMemory < linuxMinMemory {
349 return warnings, fmt.Errorf("Minimum kernel memory limit allowed is 4MB")
351 if resources.KernelMemory > 0 && !kernel.CheckKernelVersion(4, 0, 0) {
352 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.")
353 logrus.Warn("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.")
355 if resources.OomKillDisable != nil && !sysInfo.OomKillDisable {
356 // only produce warnings if the setting wasn't to *disable* the OOM Kill; no point
357 // warning the caller if they already wanted the feature to be off
358 if *resources.OomKillDisable {
359 warnings = append(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.")
360 logrus.Warn("Your kernel does not support OomKillDisable. OomKillDisable discarded.")
362 resources.OomKillDisable = nil
365 if resources.PidsLimit != 0 && !sysInfo.PidsLimit {
366 warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
367 logrus.Warn("Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
368 resources.PidsLimit = 0
371 // cpu subsystem checks and adjustments
372 if resources.NanoCPUs > 0 && resources.CPUPeriod > 0 {
373 return warnings, fmt.Errorf("Conflicting options: Nano CPUs and CPU Period cannot both be set")
375 if resources.NanoCPUs > 0 && resources.CPUQuota > 0 {
376 return warnings, fmt.Errorf("Conflicting options: Nano CPUs and CPU Quota cannot both be set")
378 if resources.NanoCPUs > 0 && (!sysInfo.CPUCfsPeriod || !sysInfo.CPUCfsQuota) {
379 return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU cfs period/quota or the cgroup is not mounted")
381 // The highest precision we could get on Linux is 0.001, by setting
382 // cpu.cfs_period_us=1000ms
384 // See the following link for details:
385 // https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
386 // Here we don't set the lower limit and it is up to the underlying platform (e.g., Linux) to return an error.
387 // The error message is 0.01 so that this is consistent with Windows
388 if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
389 return warnings, fmt.Errorf("Range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU())
392 if resources.CPUShares > 0 && !sysInfo.CPUShares {
393 warnings = append(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.")
394 logrus.Warn("Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.")
395 resources.CPUShares = 0
397 if resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod {
398 warnings = append(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.")
399 logrus.Warn("Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.")
400 resources.CPUPeriod = 0
402 if resources.CPUPeriod != 0 && (resources.CPUPeriod < 1000 || resources.CPUPeriod > 1000000) {
403 return warnings, fmt.Errorf("CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
405 if resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota {
406 warnings = append(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
407 logrus.Warn("Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
408 resources.CPUQuota = 0
410 if resources.CPUQuota > 0 && resources.CPUQuota < 1000 {
411 return warnings, fmt.Errorf("CPU cfs quota can not be less than 1ms (i.e. 1000)")
413 if resources.CPUPercent > 0 {
414 warnings = append(warnings, fmt.Sprintf("%s does not support CPU percent. Percent discarded.", runtime.GOOS))
415 logrus.Warnf("%s does not support CPU percent. Percent discarded.", runtime.GOOS)
416 resources.CPUPercent = 0
419 // cpuset subsystem checks and adjustments
420 if (resources.CpusetCpus != "" || resources.CpusetMems != "") && !sysInfo.Cpuset {
421 warnings = append(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. Cpuset discarded.")
422 logrus.Warn("Your kernel does not support cpuset or the cgroup is not mounted. Cpuset discarded.")
423 resources.CpusetCpus = ""
424 resources.CpusetMems = ""
426 cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(resources.CpusetCpus)
428 return warnings, fmt.Errorf("Invalid value %s for cpuset cpus", resources.CpusetCpus)
431 return warnings, fmt.Errorf("Requested CPUs are not available - requested %s, available: %s", resources.CpusetCpus, sysInfo.Cpus)
433 memsAvailable, err := sysInfo.IsCpusetMemsAvailable(resources.CpusetMems)
435 return warnings, fmt.Errorf("Invalid value %s for cpuset mems", resources.CpusetMems)
438 return warnings, fmt.Errorf("Requested memory nodes are not available - requested %s, available: %s", resources.CpusetMems, sysInfo.Mems)
441 // blkio subsystem checks and adjustments
442 if resources.BlkioWeight > 0 && !sysInfo.BlkioWeight {
443 warnings = append(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.")
444 logrus.Warn("Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.")
445 resources.BlkioWeight = 0
447 if resources.BlkioWeight > 0 && (resources.BlkioWeight < 10 || resources.BlkioWeight > 1000) {
448 return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000")
450 if resources.IOMaximumBandwidth != 0 || resources.IOMaximumIOps != 0 {
451 return warnings, fmt.Errorf("Invalid QoS settings: %s does not support Maximum IO Bandwidth or Maximum IO IOps", runtime.GOOS)
453 if len(resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
454 warnings = append(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.")
455 logrus.Warn("Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.")
456 resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{}
458 if len(resources.BlkioDeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice {
459 warnings = append(warnings, "Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded.")
460 logrus.Warn("Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded")
461 resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{}
463 if len(resources.BlkioDeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice {
464 warnings = append(warnings, "Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.")
465 logrus.Warn("Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.")
466 resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{}
468 if len(resources.BlkioDeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice {
469 warnings = append(warnings, "Your kernel does not support IOPS Block read limit or the cgroup is not mounted. Block I/O IOPS read limit discarded.")
470 logrus.Warn("Your kernel does not support IOPS Block I/O read limit in IO or the cgroup is not mounted. Block I/O IOPS read limit discarded.")
471 resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{}
473 if len(resources.BlkioDeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice {
474 warnings = append(warnings, "Your kernel does not support IOPS Block write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.")
475 logrus.Warn("Your kernel does not support IOPS Block I/O write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.")
476 resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{}
482 func (daemon *Daemon) getCgroupDriver() string {
483 cgroupDriver := cgroupFsDriver
485 if UsingSystemd(daemon.configStore) {
486 cgroupDriver = cgroupSystemdDriver
491 // getCD gets the raw value of the native.cgroupdriver option, if set.
492 func getCD(config *config.Config) string {
493 for _, option := range config.ExecOptions {
494 key, val, err := parsers.ParseKeyValueOpt(option)
495 if err != nil || !strings.EqualFold(key, "native.cgroupdriver") {
503 // VerifyCgroupDriver validates native.cgroupdriver
504 func VerifyCgroupDriver(config *config.Config) error {
506 if cd == "" || cd == cgroupFsDriver || cd == cgroupSystemdDriver {
509 return fmt.Errorf("native.cgroupdriver option %s not supported", cd)
512 // UsingSystemd returns true if cli option includes native.cgroupdriver=systemd
513 func UsingSystemd(config *config.Config) bool {
514 return getCD(config) == cgroupSystemdDriver
517 // verifyPlatformContainerSettings performs platform-specific validation of the
518 // hostconfig and config structures.
519 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
520 var warnings []string
521 sysInfo := sysinfo.New(true)
523 warnings, err := daemon.verifyExperimentalContainerSettings(hostConfig, config)
528 w, err := verifyContainerResources(&hostConfig.Resources, sysInfo, update)
530 // no matter err is nil or not, w could have data in itself.
531 warnings = append(warnings, w...)
537 if hostConfig.ShmSize < 0 {
538 return warnings, fmt.Errorf("SHM size can not be less than 0")
541 if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 {
542 return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj)
545 // ip-forwarding does not affect container with '--net=host' (or '--net=none')
546 if sysInfo.IPv4ForwardingDisabled && !(hostConfig.NetworkMode.IsHost() || hostConfig.NetworkMode.IsNone()) {
547 warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.")
548 logrus.Warn("IPv4 forwarding is disabled. Networking will not work")
550 // check for various conflicting options with user namespaces
551 if daemon.configStore.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() {
552 if hostConfig.Privileged {
553 return warnings, fmt.Errorf("Privileged mode is incompatible with user namespaces")
555 if hostConfig.NetworkMode.IsHost() && !hostConfig.UsernsMode.IsHost() {
556 return warnings, fmt.Errorf("Cannot share the host's network namespace when user namespaces are enabled")
558 if hostConfig.PidMode.IsHost() && !hostConfig.UsernsMode.IsHost() {
559 return warnings, fmt.Errorf("Cannot share the host PID namespace when user namespaces are enabled")
562 if hostConfig.CgroupParent != "" && UsingSystemd(daemon.configStore) {
563 // CgroupParent for systemd cgroup should be named as "xxx.slice"
564 if len(hostConfig.CgroupParent) <= 6 || !strings.HasSuffix(hostConfig.CgroupParent, ".slice") {
565 return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
568 if hostConfig.Runtime == "" {
569 hostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
572 if rt := daemon.configStore.GetRuntime(hostConfig.Runtime); rt == nil {
573 return warnings, fmt.Errorf("Unknown runtime specified %s", hostConfig.Runtime)
576 for dest := range hostConfig.Tmpfs {
577 if err := volume.ValidateTmpfsMountDestination(dest); err != nil {
585 // reloadPlatform updates configuration with platform specific options
586 // and updates the passed attributes
587 func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string]string) {
588 if conf.IsValueSet("runtimes") {
589 daemon.configStore.Runtimes = conf.Runtimes
590 // Always set the default one
591 daemon.configStore.Runtimes[config.StockRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary}
594 if conf.DefaultRuntime != "" {
595 daemon.configStore.DefaultRuntime = conf.DefaultRuntime
598 if conf.IsValueSet("default-shm-size") {
599 daemon.configStore.ShmSize = conf.ShmSize
603 var runtimeList bytes.Buffer
604 for name, rt := range daemon.configStore.Runtimes {
605 if runtimeList.Len() > 0 {
606 runtimeList.WriteRune(' ')
608 runtimeList.WriteString(fmt.Sprintf("%s:%s", name, rt))
611 attributes["runtimes"] = runtimeList.String()
612 attributes["default-runtime"] = daemon.configStore.DefaultRuntime
613 attributes["default-shm-size"] = fmt.Sprintf("%d", daemon.configStore.ShmSize)
616 // verifyDaemonSettings performs validation of daemon config struct
617 func verifyDaemonSettings(conf *config.Config) error {
618 // Check for mutually incompatible config options
619 if conf.BridgeConfig.Iface != "" && conf.BridgeConfig.IP != "" {
620 return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one")
622 if !conf.BridgeConfig.EnableIPTables && !conf.BridgeConfig.InterContainerCommunication {
623 return fmt.Errorf("You specified --iptables=false with --icc=false. ICC=false uses iptables to function. Please set --icc or --iptables to true")
625 if !conf.BridgeConfig.EnableIPTables && conf.BridgeConfig.EnableIPMasq {
626 conf.BridgeConfig.EnableIPMasq = false
628 if err := VerifyCgroupDriver(conf); err != nil {
631 if conf.CgroupParent != "" && UsingSystemd(conf) {
632 if len(conf.CgroupParent) <= 6 || !strings.HasSuffix(conf.CgroupParent, ".slice") {
633 return fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
637 if conf.DefaultRuntime == "" {
638 conf.DefaultRuntime = config.StockRuntimeName
640 if conf.Runtimes == nil {
641 conf.Runtimes = make(map[string]types.Runtime)
643 conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary}
644 conf.Runtimes["bare"] = types.Runtime{}
649 // checkSystem validates platform-specific requirements
650 func checkSystem() error {
651 if os.Geteuid() != 0 {
652 return fmt.Errorf("The Docker daemon needs to be run as root")
657 // configureMaxThreads sets the Go runtime max threads threshold
658 // which is 90% of the kernel setting from /proc/sys/kernel/threads-max
659 func configureMaxThreads(config *config.Config) error {
660 mt, err := ioutil.ReadFile("/proc/sys/kernel/threads-max")
664 mtint, err := strconv.Atoi(strings.TrimSpace(string(mt)))
668 maxThreads := (mtint / 100) * 90
669 debug.SetMaxThreads(maxThreads)
670 logrus.Debugf("Golang's threads limit set to %d", maxThreads)
674 func overlaySupportsSelinux() (bool, error) {
675 f, err := os.Open("/proc/kallsyms")
677 if os.IsNotExist(err) {
684 var symAddr, symType, symName, text string
686 s := bufio.NewScanner(f)
688 if err := s.Err(); err != nil {
693 if _, err := fmt.Sscanf(text, "%s %s %s", &symAddr, &symType, &symName); err != nil {
694 return false, fmt.Errorf("Scanning '%s' failed: %s", text, err)
697 // Check for presence of symbol security_inode_copy_up.
698 if symName == "security_inode_copy_up" {
705 // configureKernelSecuritySupport configures and validates security support for the kernel
706 func configureKernelSecuritySupport(config *config.Config, driverNames []string) error {
707 if config.EnableSelinuxSupport {
708 if !selinuxEnabled() {
709 logrus.Warn("Docker could not enable SELinux on the host system")
713 overlayFound := false
714 for _, d := range driverNames {
715 if d == "overlay" || d == "overlay2" {
722 // If driver is overlay or overlay2, make sure kernel
723 // supports selinux with overlay.
724 supported, err := overlaySupportsSelinux()
730 logrus.Warnf("SELinux is not supported with the %v graph driver on this kernel", driverNames)
739 func (daemon *Daemon) initNetworkController(config *config.Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) {
740 netOptions, err := daemon.networkOptions(config, daemon.PluginStore, activeSandboxes)
745 controller, err := libnetwork.New(netOptions...)
747 return nil, fmt.Errorf("error obtaining controller instance: %v", err)
750 if len(activeSandboxes) > 0 {
751 logrus.Info("There are old running containers, the network config will not take affect")
752 return controller, nil
755 // Initialize default network on "null"
756 if n, _ := controller.NetworkByName("none"); n == nil {
757 if _, err := controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(true)); err != nil {
758 return nil, fmt.Errorf("Error creating default \"null\" network: %v", err)
762 // Initialize default network on "host"
763 if n, _ := controller.NetworkByName("host"); n == nil {
764 if _, err := controller.NewNetwork("host", "host", "", libnetwork.NetworkOptionPersist(true)); err != nil {
765 return nil, fmt.Errorf("Error creating default \"host\" network: %v", err)
769 // Clear stale bridge network
770 if n, err := controller.NetworkByName("bridge"); err == nil {
771 if err = n.Delete(); err != nil {
772 return nil, fmt.Errorf("could not delete the default bridge network: %v", err)
776 if !config.DisableBridge {
777 // Initialize default driver "bridge"
778 if err := initBridgeDriver(controller, config); err != nil {
782 removeDefaultBridgeInterface()
785 return controller, nil
788 func driverOptions(config *config.Config) []nwconfig.Option {
789 bridgeConfig := options.Generic{
790 "EnableIPForwarding": config.BridgeConfig.EnableIPForward,
791 "EnableIPTables": config.BridgeConfig.EnableIPTables,
792 "EnableUserlandProxy": config.BridgeConfig.EnableUserlandProxy,
793 "UserlandProxyPath": config.BridgeConfig.UserlandProxyPath}
794 bridgeOption := options.Generic{netlabel.GenericData: bridgeConfig}
796 dOptions := []nwconfig.Option{}
797 dOptions = append(dOptions, nwconfig.OptionDriverConfig("bridge", bridgeOption))
801 func initBridgeDriver(controller libnetwork.NetworkController, config *config.Config) error {
802 bridgeName := bridge.DefaultBridgeName
803 if config.BridgeConfig.Iface != "" {
804 bridgeName = config.BridgeConfig.Iface
806 netOption := map[string]string{
807 bridge.BridgeName: bridgeName,
808 bridge.DefaultBridge: strconv.FormatBool(true),
809 netlabel.DriverMTU: strconv.Itoa(config.Mtu),
810 bridge.EnableIPMasquerade: strconv.FormatBool(config.BridgeConfig.EnableIPMasq),
811 bridge.EnableICC: strconv.FormatBool(config.BridgeConfig.InterContainerCommunication),
815 if config.BridgeConfig.DefaultIP != nil {
816 netOption[bridge.DefaultBindingIP] = config.BridgeConfig.DefaultIP.String()
820 ipamV4Conf *libnetwork.IpamConf
821 ipamV6Conf *libnetwork.IpamConf
824 ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
826 nwList, nw6List, err := netutils.ElectInterfaceAddresses(bridgeName)
828 return errors.Wrap(err, "list bridge addresses failed")
832 if len(nwList) > 1 && config.BridgeConfig.FixedCIDR != "" {
833 _, fCIDR, err := net.ParseCIDR(config.BridgeConfig.FixedCIDR)
835 return errors.Wrap(err, "parse CIDR failed")
837 // Iterate through in case there are multiple addresses for the bridge
838 for _, entry := range nwList {
839 if fCIDR.Contains(entry.IP) {
846 ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String()
847 hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask)
848 if hip.IsGlobalUnicast() {
849 ipamV4Conf.Gateway = nw.IP.String()
852 if config.BridgeConfig.IP != "" {
853 ipamV4Conf.PreferredPool = config.BridgeConfig.IP
854 ip, _, err := net.ParseCIDR(config.BridgeConfig.IP)
858 ipamV4Conf.Gateway = ip.String()
859 } else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" {
860 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)
863 if config.BridgeConfig.FixedCIDR != "" {
864 _, fCIDR, err := net.ParseCIDR(config.BridgeConfig.FixedCIDR)
869 ipamV4Conf.SubPool = fCIDR.String()
872 if config.BridgeConfig.DefaultGatewayIPv4 != nil {
873 ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.BridgeConfig.DefaultGatewayIPv4.String()
876 var deferIPv6Alloc bool
877 if config.BridgeConfig.FixedCIDRv6 != "" {
878 _, fCIDRv6, err := net.ParseCIDR(config.BridgeConfig.FixedCIDRv6)
883 // In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has
884 // at least 48 host bits, we need to guarantee the current behavior where the containers'
885 // IPv6 addresses will be constructed based on the containers' interface MAC address.
886 // We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints
887 // on this network until after the driver has created the endpoint and returned the
888 // constructed address. Libnetwork will then reserve this address with the ipam driver.
889 ones, _ := fCIDRv6.Mask.Size()
890 deferIPv6Alloc = ones <= 80
892 if ipamV6Conf == nil {
893 ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
895 ipamV6Conf.PreferredPool = fCIDRv6.String()
897 // In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6
898 // address belongs to the same network, we need to inform libnetwork about it, so
899 // that it can be reserved with IPAM and it will not be given away to somebody else
900 for _, nw6 := range nw6List {
901 if fCIDRv6.Contains(nw6.IP) {
902 ipamV6Conf.Gateway = nw6.IP.String()
908 if config.BridgeConfig.DefaultGatewayIPv6 != nil {
909 if ipamV6Conf == nil {
910 ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
912 ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = config.BridgeConfig.DefaultGatewayIPv6.String()
915 v4Conf := []*libnetwork.IpamConf{ipamV4Conf}
916 v6Conf := []*libnetwork.IpamConf{}
917 if ipamV6Conf != nil {
918 v6Conf = append(v6Conf, ipamV6Conf)
920 // Initialize default network on "bridge" with the same name
921 _, err = controller.NewNetwork("bridge", "bridge", "",
922 libnetwork.NetworkOptionEnableIPv6(config.BridgeConfig.EnableIPv6),
923 libnetwork.NetworkOptionDriverOpts(netOption),
924 libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil),
925 libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
927 return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
932 // Remove default bridge interface if present (--bridge=none use case)
933 func removeDefaultBridgeInterface() {
934 if lnk, err := netlink.LinkByName(bridge.DefaultBridgeName); err == nil {
935 if err := netlink.LinkDel(lnk); err != nil {
936 logrus.Warnf("Failed to remove bridge interface (%s): %v", bridge.DefaultBridgeName, err)
941 func (daemon *Daemon) getLayerInit() func(string) error {
942 return daemon.setupInitLayer
945 // Parse the remapped root (user namespace) option, which can be one of:
946 // username - valid username from /etc/passwd
947 // username:groupname - valid username; valid groupname from /etc/group
948 // uid - 32-bit unsigned int valid Linux UID value
949 // uid:gid - uid value; 32-bit unsigned int Linux GID value
951 // If no groupname is specified, and a username is specified, an attempt
952 // will be made to lookup a gid for that username as a groupname
954 // If names are used, they are verified to exist in passwd/group
955 func parseRemappedRoot(usergrp string) (string, string, error) {
959 username, groupname string
962 idparts := strings.Split(usergrp, ":")
963 if len(idparts) > 2 {
964 return "", "", fmt.Errorf("Invalid user/group specification in --userns-remap: %q", usergrp)
967 if uid, err := strconv.ParseInt(idparts[0], 10, 32); err == nil {
968 // must be a uid; take it as valid
970 luser, err := idtools.LookupUID(userID)
972 return "", "", fmt.Errorf("Uid %d has no entry in /etc/passwd: %v", userID, err)
974 username = luser.Name
975 if len(idparts) == 1 {
976 // if the uid was numeric and no gid was specified, take the uid as the gid
978 lgrp, err := idtools.LookupGID(groupID)
980 return "", "", fmt.Errorf("Gid %d has no entry in /etc/group: %v", groupID, err)
982 groupname = lgrp.Name
985 lookupName := idparts[0]
986 // special case: if the user specified "default", they want Docker to create or
987 // use (after creation) the "dockremap" user/group for root remapping
988 if lookupName == defaultIDSpecifier {
989 lookupName = defaultRemappedID
991 luser, err := idtools.LookupUser(lookupName)
992 if err != nil && idparts[0] != defaultIDSpecifier {
993 // error if the name requested isn't the special "dockremap" ID
994 return "", "", fmt.Errorf("Error during uid lookup for %q: %v", lookupName, err)
995 } else if err != nil {
996 // special case-- if the username == "default", then we have been asked
997 // to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid}
998 // ranges will be used for the user and group mappings in user namespaced containers
999 _, _, err := idtools.AddNamespaceRangesUser(defaultRemappedID)
1001 return defaultRemappedID, defaultRemappedID, nil
1003 return "", "", fmt.Errorf("Error during %q user creation: %v", defaultRemappedID, err)
1005 username = luser.Name
1006 if len(idparts) == 1 {
1007 // we only have a string username, and no group specified; look up gid from username as group
1008 group, err := idtools.LookupGroup(lookupName)
1010 return "", "", fmt.Errorf("Error during gid lookup for %q: %v", lookupName, err)
1012 groupname = group.Name
1016 if len(idparts) == 2 {
1017 // groupname or gid is separately specified and must be resolved
1018 // to an unsigned 32-bit gid
1019 if gid, err := strconv.ParseInt(idparts[1], 10, 32); err == nil {
1020 // must be a gid, take it as valid
1022 lgrp, err := idtools.LookupGID(groupID)
1024 return "", "", fmt.Errorf("Gid %d has no entry in /etc/passwd: %v", groupID, err)
1026 groupname = lgrp.Name
1028 // not a number; attempt a lookup
1029 if _, err := idtools.LookupGroup(idparts[1]); err != nil {
1030 return "", "", fmt.Errorf("Error during groupname lookup for %q: %v", idparts[1], err)
1032 groupname = idparts[1]
1035 return username, groupname, nil
1038 func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
1039 if runtime.GOOS != "linux" && config.RemappedRoot != "" {
1040 return nil, fmt.Errorf("User namespaces are only supported on Linux")
1043 // if the daemon was started with remapped root option, parse
1044 // the config option to the int uid,gid values
1045 if config.RemappedRoot != "" {
1046 username, groupname, err := parseRemappedRoot(config.RemappedRoot)
1050 if username == "root" {
1051 // Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
1053 logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
1054 return &idtools.IDMappings{}, nil
1056 logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname)
1057 // update remapped root setting now that we have resolved them to actual names
1058 config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname)
1060 mappings, err := idtools.NewIDMappings(username, groupname)
1062 return nil, errors.Wrapf(err, "Can't create ID mappings: %v")
1064 return mappings, nil
1066 return &idtools.IDMappings{}, nil
1069 func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
1070 config.Root = rootDir
1071 // the docker root metadata directory needs to have execute permissions for all users (g+x,o+x)
1072 // so that syscalls executing as non-root, operating on subdirectories of the graph root
1073 // (e.g. mounted layers of a container) can traverse this path.
1074 // The user namespace support will create subdirectories for the remapped root host uid:gid
1075 // pair owned by that same uid:gid pair for proper write access to those needed metadata and
1076 // layer content subtrees.
1077 if _, err := os.Stat(rootDir); err == nil {
1078 // root current exists; verify the access bits are correct by setting them
1079 if err = os.Chmod(rootDir, 0711); err != nil {
1082 } else if os.IsNotExist(err) {
1083 // no root exists yet, create it 0711 with root:root ownership
1084 if err := os.MkdirAll(rootDir, 0711); err != nil {
1089 // if user namespaces are enabled we will create a subtree underneath the specified root
1090 // with any/all specified remapped root uid/gid options on the daemon creating
1091 // a new subdirectory with ownership set to the remapped uid/gid (so as to allow
1092 // `chdir()` to work for containers namespaced to that uid/gid)
1093 if config.RemappedRoot != "" {
1094 config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIDs.UID, rootIDs.GID))
1095 logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
1096 // Create the root directory if it doesn't exist
1097 if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIDs); err != nil {
1098 return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err)
1100 // we also need to verify that any pre-existing directories in the path to
1101 // the graphroot won't block access to remapped root--if any pre-existing directory
1102 // has strict permissions that don't allow "x", container start will fail, so
1103 // better to warn and fail now
1104 dirPath := config.Root
1106 dirPath = filepath.Dir(dirPath)
1110 if !idtools.CanAccess(dirPath, rootIDs) {
1111 return fmt.Errorf("A subdirectory in your graphroot path (%s) restricts access to the remapped root uid/gid; please fix by allowing 'o+x' permissions on existing directories.", config.Root)
1118 // registerLinks writes the links to a file.
1119 func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
1120 if hostConfig == nil || hostConfig.NetworkMode.IsUserDefined() {
1124 for _, l := range hostConfig.Links {
1125 name, alias, err := opts.ParseLink(l)
1129 child, err := daemon.GetContainer(name)
1131 return fmt.Errorf("Could not get container for %s", name)
1133 for child.HostConfig.NetworkMode.IsContainer() {
1134 parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2)
1135 child, err = daemon.GetContainer(parts[1])
1137 return fmt.Errorf("Could not get container for %s", parts[1])
1140 if child.HostConfig.NetworkMode.IsHost() {
1141 return runconfig.ErrConflictHostNetworkAndLinks
1143 if err := daemon.registerLink(container, child, alias); err != nil {
1148 // After we load all the links into the daemon
1149 // set them to nil on the hostconfig
1150 _, err := container.WriteHostConfig()
1154 // conditionalMountOnStart is a platform specific helper function during the
1155 // container start to call mount.
1156 func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
1157 return daemon.Mount(container)
1160 // conditionalUnmountOnCleanup is a platform specific helper function called
1161 // during the cleanup of a container to unmount.
1162 func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
1163 return daemon.Unmount(container)
1166 func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
1168 return nil, errNotRunning{c.ID}
1170 stats, err := daemon.containerd.Stats(c.ID)
1174 s := &types.StatsJSON{}
1175 cgs := stats.CgroupStats
1177 s.BlkioStats = types.BlkioStats{
1178 IoServiceBytesRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceBytesRecursive),
1179 IoServicedRecursive: copyBlkioEntry(cgs.BlkioStats.IoServicedRecursive),
1180 IoQueuedRecursive: copyBlkioEntry(cgs.BlkioStats.IoQueuedRecursive),
1181 IoServiceTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceTimeRecursive),
1182 IoWaitTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoWaitTimeRecursive),
1183 IoMergedRecursive: copyBlkioEntry(cgs.BlkioStats.IoMergedRecursive),
1184 IoTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoTimeRecursive),
1185 SectorsRecursive: copyBlkioEntry(cgs.BlkioStats.SectorsRecursive),
1188 s.CPUStats = types.CPUStats{
1189 CPUUsage: types.CPUUsage{
1190 TotalUsage: cpu.CpuUsage.TotalUsage,
1191 PercpuUsage: cpu.CpuUsage.PercpuUsage,
1192 UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode,
1193 UsageInUsermode: cpu.CpuUsage.UsageInUsermode,
1195 ThrottlingData: types.ThrottlingData{
1196 Periods: cpu.ThrottlingData.Periods,
1197 ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods,
1198 ThrottledTime: cpu.ThrottlingData.ThrottledTime,
1201 mem := cgs.MemoryStats.Usage
1202 s.MemoryStats = types.MemoryStats{
1204 MaxUsage: mem.MaxUsage,
1205 Stats: cgs.MemoryStats.Stats,
1206 Failcnt: mem.Failcnt,
1209 // if the container does not set memory limit, use the machineMemory
1210 if mem.Limit > daemon.machineMemory && daemon.machineMemory > 0 {
1211 s.MemoryStats.Limit = daemon.machineMemory
1213 if cgs.PidsStats != nil {
1214 s.PidsStats = types.PidsStats{
1215 Current: cgs.PidsStats.Current,
1219 s.Read, err = ptypes.Timestamp(stats.Timestamp)
1226 // setDefaultIsolation determines the default isolation mode for the
1227 // daemon to run in. This is only applicable on Windows
1228 func (daemon *Daemon) setDefaultIsolation() error {
1232 func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
1234 for _, l := range rootfs.DiffIDs {
1235 layers = append(layers, l.String())
1237 return types.RootFS{
1243 // setupDaemonProcess sets various settings for the daemon's process
1244 func setupDaemonProcess(config *config.Config) error {
1245 // setup the daemons oom_score_adj
1246 return setupOOMScoreAdj(config.OOMScoreAdjust)
1249 func setupOOMScoreAdj(score int) error {
1250 f, err := os.OpenFile("/proc/self/oom_score_adj", os.O_WRONLY, 0)
1255 stringScore := strconv.Itoa(score)
1256 _, err = f.WriteString(stringScore)
1257 if os.IsPermission(err) {
1258 // Setting oom_score_adj does not work in an
1259 // unprivileged container. Ignore the error, but log
1260 // it if we appear not to be in that situation.
1261 if !rsystem.RunningInUserNS() {
1262 logrus.Debugf("Permission denied writing %q to /proc/self/oom_score_adj", stringScore)
1270 func (daemon *Daemon) initCgroupsPath(path string) error {
1271 if path == "/" || path == "." {
1275 if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 {
1279 // Recursively create cgroup to ensure that the system and all parent cgroups have values set
1280 // for the period and runtime as this limits what the children can be set to.
1281 daemon.initCgroupsPath(filepath.Dir(path))
1283 mnt, root, err := cgroups.FindCgroupMountpointAndRoot("cpu")
1287 // When docker is run inside docker, the root is based of the host cgroup.
1288 // Should this be handled in runc/libcontainer/cgroups ?
1289 if strings.HasPrefix(root, "/docker/") {
1293 path = filepath.Join(mnt, root, path)
1294 sysinfo := sysinfo.New(true)
1295 if err := maybeCreateCPURealTimeFile(sysinfo.CPURealtimePeriod, daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
1298 if err := maybeCreateCPURealTimeFile(sysinfo.CPURealtimeRuntime, daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path); err != nil {
1304 func maybeCreateCPURealTimeFile(sysinfoPresent bool, configValue int64, file string, path string) error {
1305 if sysinfoPresent && configValue != 0 {
1306 if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
1309 if err := ioutil.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700); err != nil {
1316 func (daemon *Daemon) setupSeccompProfile() error {
1317 if daemon.configStore.SeccompProfile != "" {
1318 daemon.seccompProfilePath = daemon.configStore.SeccompProfile
1319 b, err := ioutil.ReadFile(daemon.configStore.SeccompProfile)
1321 return fmt.Errorf("opening seccomp profile (%s) failed: %v", daemon.configStore.SeccompProfile, err)
1323 daemon.seccompProfile = b