daemon/daemon_unix.go
10d30c64
 // +build linux freebsd
8fb0ca2c
 
4f0d95fa
 package daemon // import "github.com/docker/docker/daemon"
8fb0ca2c
 
 import (
885b29df
 	"bufio"
ddae20c0
 	"context"
8fb0ca2c
 	"fmt"
140a7434
 	"io/ioutil"
8fb0ca2c
 	"net"
 	"os"
 	"path/filepath"
557c7cb8
 	"runtime"
140a7434
 	"runtime/debug"
0f351ce3
 	"strconv"
8fb0ca2c
 	"strings"
846baf1f
 	"time"
8fb0ca2c
 
ddae20c0
 	containerd_cgroups "github.com/containerd/cgroups"
91e197d6
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/blkiodev"
 	pblkiodev "github.com/docker/docker/api/types/blkiodev"
 	containertypes "github.com/docker/docker/api/types/container"
6bb0d181
 	"github.com/docker/docker/container"
db63f937
 	"github.com/docker/docker/daemon/config"
c502bcff
 	"github.com/docker/docker/daemon/initlayer"
1e0234dd
 	"github.com/docker/docker/errdefs"
c424be21
 	"github.com/docker/docker/opts"
7a7357da
 	"github.com/docker/docker/pkg/containerfs"
442b4562
 	"github.com/docker/docker/pkg/idtools"
ddae20c0
 	"github.com/docker/docker/pkg/ioutils"
a510192b
 	"github.com/docker/docker/pkg/mount"
5ce5a8e9
 	"github.com/docker/docker/pkg/parsers"
8fb0ca2c
 	"github.com/docker/docker/pkg/parsers/kernel"
b2d06b6f
 	"github.com/docker/docker/pkg/sysinfo"
8fb0ca2c
 	"github.com/docker/docker/runconfig"
6a70fd22
 	volumemounts "github.com/docker/docker/volume/mounts"
8fb0ca2c
 	"github.com/docker/libnetwork"
da5a3e6d
 	nwconfig "github.com/docker/libnetwork/config"
0f351ce3
 	"github.com/docker/libnetwork/drivers/bridge"
8fb0ca2c
 	"github.com/docker/libnetwork/netlabel"
6eb2b903
 	"github.com/docker/libnetwork/netutils"
8fb0ca2c
 	"github.com/docker/libnetwork/options"
9c4570a9
 	lntypes "github.com/docker/libnetwork/types"
56f77d5a
 	"github.com/opencontainers/runc/libcontainer/cgroups"
9ed54d3c
 	rsystem "github.com/opencontainers/runc/libcontainer/system"
f23c00d8
 	"github.com/opencontainers/runtime-spec/specs-go"
abbbf914
 	"github.com/opencontainers/selinux/go-selinux/label"
e9c4c513
 	"github.com/pkg/errors"
1009e6a4
 	"github.com/sirupsen/logrus"
a0af884d
 	"github.com/vishvananda/netlink"
01f70b02
 	"golang.org/x/sys/unix"
8fb0ca2c
 )
 
e0af23dc
 const (
ddae20c0
 	// DefaultShimBinary is the default shim to be used by containerd if none
 	// is specified
34eede02
 	DefaultShimBinary = "containerd-shim"
ddae20c0
 
 	// DefaultRuntimeBinary is the default runtime to be used by
 	// containerd if none is specified
34eede02
 	DefaultRuntimeBinary = "runc"
ddae20c0
 
e0af23dc
 	// See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269
 	linuxMinCPUShares = 2
 	linuxMaxCPUShares = 262144
10d30c64
 	platformSupported = true
2347f980
 	// It's not kernel limit, we want this 4M limit to supply a reasonable functional container
 	linuxMinMemory = 4194304
557c7cb8
 	// constants for remapped root settings
f23c00d8
 	defaultIDSpecifier = "default"
 	defaultRemappedID  = "dockremap"
8af4f89c
 
 	// constant for cgroup drivers
 	cgroupFsDriver      = "cgroupfs"
 	cgroupSystemdDriver = "systemd"
57b59f87
 	cgroupNoneDriver    = "none"
ddae20c0
 
 	// DefaultRuntimeName is the default runtime to be used by
 	// containerd if none is specified
34eede02
 	DefaultRuntimeName = "runc"
e0af23dc
 )
 
b6e5ea8e
 type containerGetter interface {
 	GetContainer(string) (*container.Container, error)
 }
 
005506d3
 func getMemoryResources(config containertypes.Resources) *specs.LinuxMemory {
 	memory := specs.LinuxMemory{}
9c4570a9
 
 	if config.Memory > 0 {
45d85c99
 		memory.Limit = &config.Memory
9c4570a9
 	}
 
 	if config.MemoryReservation > 0 {
45d85c99
 		memory.Reservation = &config.MemoryReservation
9c4570a9
 	}
 
005506d3
 	if config.MemorySwap > 0 {
45d85c99
 		memory.Swap = &config.MemorySwap
9c4570a9
 	}
 
 	if config.MemorySwappiness != nil {
 		swappiness := uint64(*config.MemorySwappiness)
 		memory.Swappiness = &swappiness
 	}
 
44b074d1
 	if config.OomKillDisable != nil {
 		memory.DisableOOMKiller = config.OomKillDisable
 	}
 
9c4570a9
 	if config.KernelMemory != 0 {
45d85c99
 		memory.Kernel = &config.KernelMemory
9c4570a9
 	}
 
f0238166
 	if config.KernelMemoryTCP != 0 {
 		memory.KernelTCP = &config.KernelMemoryTCP
 	}
 
9c4570a9
 	return &memory
 }
 
74eb258f
 func getPidsLimit(config containertypes.Resources) *specs.LinuxPids {
ffa1728d
 	if config.PidsLimit == nil {
 		return nil
 	}
 	if *config.PidsLimit <= 0 {
 		// docker API allows 0 and negative values to unset this to be consistent
 		// with default values. When updating values, runc requires -1 to unset
 		// the previous limit.
 		return &specs.LinuxPids{Limit: -1}
74eb258f
 	}
ffa1728d
 	return &specs.LinuxPids{Limit: *config.PidsLimit}
74eb258f
 }
 
005506d3
 func getCPUResources(config containertypes.Resources) (*specs.LinuxCPU, error) {
 	cpu := specs.LinuxCPU{}
9c4570a9
 
005506d3
 	if config.CPUShares < 0 {
 		return nil, fmt.Errorf("shares: invalid argument")
 	}
 	if config.CPUShares >= 0 {
9c4570a9
 		shares := uint64(config.CPUShares)
 		cpu.Shares = &shares
 	}
 
 	if config.CpusetCpus != "" {
005506d3
 		cpu.Cpus = config.CpusetCpus
9c4570a9
 	}
 
 	if config.CpusetMems != "" {
005506d3
 		cpu.Mems = config.CpusetMems
9c4570a9
 	}
 
846baf1f
 	if config.NanoCPUs > 0 {
 		// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
aff99136
 		period := uint64(100 * time.Millisecond / time.Microsecond)
005506d3
 		quota := config.NanoCPUs * int64(period) / 1e9
846baf1f
 		cpu.Period = &period
 		cpu.Quota = &quota
 	}
 
9c4570a9
 	if config.CPUPeriod != 0 {
 		period := uint64(config.CPUPeriod)
 		cpu.Period = &period
 	}
 
 	if config.CPUQuota != 0 {
005506d3
 		q := config.CPUQuota
 		cpu.Quota = &q
9c4570a9
 	}
 
56f77d5a
 	if config.CPURealtimePeriod != 0 {
 		period := uint64(config.CPURealtimePeriod)
 		cpu.RealtimePeriod = &period
 	}
 
 	if config.CPURealtimeRuntime != 0 {
005506d3
 		c := config.CPURealtimeRuntime
 		cpu.RealtimeRuntime = &c
56f77d5a
 	}
 
005506d3
 	return &cpu, nil
9c4570a9
 }
 
005506d3
 func getBlkioWeightDevices(config containertypes.Resources) ([]specs.LinuxWeightDevice, error) {
01f70b02
 	var stat unix.Stat_t
005506d3
 	var blkioWeightDevices []specs.LinuxWeightDevice
0fbfa144
 
 	for _, weightDevice := range config.BlkioWeightDevice {
01f70b02
 		if err := unix.Stat(weightDevice.Path, &stat); err != nil {
0fbfa144
 			return nil, err
 		}
9c4570a9
 		weight := weightDevice.Weight
005506d3
 		d := specs.LinuxWeightDevice{Weight: &weight}
750e0ace
 		d.Major = int64(unix.Major(stat.Rdev))
 		d.Minor = int64(unix.Minor(stat.Rdev))
9c4570a9
 		blkioWeightDevices = append(blkioWeightDevices, d)
0fbfa144
 	}
 
38797ca6
 	return blkioWeightDevices, nil
0fbfa144
 }
 
d7fda019
 func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
 	container.NoNewPrivileges = daemon.configStore.NoNewPrivileges
 	return parseSecurityOpt(container, hostConfig)
 }
 
7ac4232e
 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
8fb0ca2c
 	var (
 		labelOpts []string
 		err       error
 	)
 
 	for _, opt := range config.SecurityOpt {
cb9aeb04
 		if opt == "no-new-privileges" {
 			container.NoNewPrivileges = true
a9b6319e
 			continue
 		}
005506d3
 		if opt == "disable" {
 			labelOpts = append(labelOpts, "disable")
 			continue
 		}
a9b6319e
 
 		var con []string
 		if strings.Contains(opt, "=") {
 			con = strings.SplitN(opt, "=", 2)
 		} else if strings.Contains(opt, ":") {
 			con = strings.SplitN(opt, ":", 2)
47396d63
 			logrus.Warn("Security options with `:` as a separator are deprecated and will be completely unsupported in 17.04, use `=` instead.")
a9b6319e
 		}
 		if len(con) != 2 {
 			return fmt.Errorf("invalid --security-opt 1: %q", opt)
 		}
 
 		switch con[0] {
 		case "label":
 			labelOpts = append(labelOpts, con[1])
 		case "apparmor":
 			container.AppArmorProfile = con[1]
 		case "seccomp":
 			container.SeccompProfile = con[1]
d7fda019
 		case "no-new-privileges":
 			noNewPrivileges, err := strconv.ParseBool(con[1])
 			if err != nil {
 				return fmt.Errorf("invalid --security-opt 2: %q", opt)
 			}
 			container.NoNewPrivileges = noNewPrivileges
a9b6319e
 		default:
 			return fmt.Errorf("invalid --security-opt 2: %q", opt)
8fb0ca2c
 		}
 	}
 
 	container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts)
 	return err
 }
 
005506d3
 func getBlkioThrottleDevices(devs []*blkiodev.ThrottleDevice) ([]specs.LinuxThrottleDevice, error) {
 	var throttleDevices []specs.LinuxThrottleDevice
01f70b02
 	var stat unix.Stat_t
843084b0
 
668f0a2a
 	for _, d := range devs {
01f70b02
 		if err := unix.Stat(d.Path, &stat); err != nil {
843084b0
 			return nil, err
 		}
005506d3
 		d := specs.LinuxThrottleDevice{Rate: d.Rate}
750e0ace
 		d.Major = int64(unix.Major(stat.Rdev))
 		d.Minor = int64(unix.Minor(stat.Rdev))
668f0a2a
 		throttleDevices = append(throttleDevices, d)
843084b0
 	}
 
668f0a2a
 	return throttleDevices, nil
3f15a055
 }
 
5a52917e
 // adjustParallelLimit takes a number of objects and a proposed limit and
 // figures out if it's reasonable (and adjusts it accordingly). This is only
 // used for daemon startup, which does a lot of parallel loading of containers
 // (and if we exceed RLIMIT_NOFILE then we're in trouble).
 func adjustParallelLimit(n int, limit int) int {
 	// Rule-of-thumb overhead factor (how many files will each goroutine open
 	// simultaneously). Yes, this is ugly but to be frank this whole thing is
 	// ugly.
 	const overhead = 2
 
 	// On Linux, we need to ensure that parallelStartupJobs doesn't cause us to
 	// exceed RLIMIT_NOFILE. If parallelStartupJobs is too large, we reduce it
 	// and give a warning (since in theory the user should increase their
 	// ulimits to the largest possible value for dockerd).
 	var rlim unix.Rlimit
 	if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlim); err != nil {
 		logrus.Warnf("Couldn't find dockerd's RLIMIT_NOFILE to double-check startup parallelism factor: %v", err)
 		return limit
 	}
 	softRlimit := int(rlim.Cur)
 
 	// Much fewer containers than RLIMIT_NOFILE. No need to adjust anything.
 	if softRlimit > overhead*n {
 		return limit
 	}
 
 	// RLIMIT_NOFILE big enough, no need to adjust anything.
 	if softRlimit > overhead*limit {
 		return limit
 	}
 
 	logrus.Warnf("Found dockerd's open file ulimit (%v) is far too small -- consider increasing it significantly (at least %v)", softRlimit, overhead*limit)
 	return softRlimit / overhead
 }
 
8fb0ca2c
 func checkKernel() error {
 	// Check for unsupported kernel versions
 	// FIXME: it would be cleaner to not test for specific versions, but rather
 	// test for specific functionalities.
 	// Unfortunately we can't test for the feature "does not cause a kernel panic"
 	// without actually causing a kernel panic, so we need this workaround until
 	// the circumstances of pre-3.10 crashes are clearer.
 	// For details see https://github.com/docker/docker/issues/407
51b23d88
 	// Docker 1.11 and above doesn't actually run on kernels older than 3.4,
 	// due to containerd-shim usage of PR_SET_CHILD_SUBREAPER (introduced in 3.4).
da5d66fb
 	if !kernel.CheckKernelVersion(3, 10, 0) {
87959dbf
 		v, _ := kernel.GetKernelVersion()
 		if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" {
51b23d88
 			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())
8fb0ca2c
 		}
 	}
 	return nil
 }
 
3fea79bf
 // adaptContainerSettings is called during container creation to modify any
 // settings necessary in the HostConfig structure.
7ac4232e
 func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
e0af23dc
 	if adjustCPUShares && hostConfig.CPUShares > 0 {
 		// Handle unsupported CPUShares
 		if hostConfig.CPUShares < linuxMinCPUShares {
 			logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, linuxMinCPUShares)
 			hostConfig.CPUShares = linuxMinCPUShares
 		} else if hostConfig.CPUShares > linuxMaxCPUShares {
 			logrus.Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, linuxMaxCPUShares)
 			hostConfig.CPUShares = linuxMaxCPUShares
 		}
 	}
7e0dfbf4
 	if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 {
 		// By default, MemorySwap is set to twice the size of Memory.
 		hostConfig.MemorySwap = hostConfig.Memory * 2
 	}
5190794f
 	if hostConfig.ShmSize == 0 {
db63f937
 		hostConfig.ShmSize = config.DefaultShmSize
db575ef6
 		if daemon.configStore != nil {
 			hostConfig.ShmSize = int64(daemon.configStore.ShmSize)
 		}
ef1d410b
 	}
7120976d
 	// Set default IPC mode, if unset for container
 	if hostConfig.IpcMode.IsEmpty() {
 		m := config.DefaultIpcMode
 		if daemon.configStore != nil {
 			m = daemon.configStore.IpcMode
 		}
 		hostConfig.IpcMode = containertypes.IpcMode(m)
 	}
 
b6e5ea8e
 	adaptSharedNamespaceContainer(daemon, hostConfig)
 
1415f55c
 	var err error
881e20ee
 	opts, err := daemon.generateSecurityOpt(hostConfig)
4c10c2de
 	if err != nil {
 		return err
1415f55c
 	}
4c10c2de
 	hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, opts...)
f4a68733
 	if hostConfig.OomKillDisable == nil {
 		defaultOomKillDisable := false
 		hostConfig.OomKillDisable = &defaultOomKillDisable
 	}
1415f55c
 
 	return nil
7e0dfbf4
 }
 
b6e5ea8e
 // adaptSharedNamespaceContainer replaces container name with its ID in hostConfig.
 // To be more precisely, it modifies `container:name` to `container:ID` of PidMode, IpcMode
 // and NetworkMode.
 //
 // When a container shares its namespace with another container, use ID can keep the namespace
 // sharing connection between the two containers even the another container is renamed.
 func adaptSharedNamespaceContainer(daemon containerGetter, hostConfig *containertypes.HostConfig) {
 	containerPrefix := "container:"
 	if hostConfig.PidMode.IsContainer() {
 		pidContainer := hostConfig.PidMode.Container()
 		// if there is any error returned here, we just ignore it and leave it to be
 		// handled in the following logic
 		if c, err := daemon.GetContainer(pidContainer); err == nil {
 			hostConfig.PidMode = containertypes.PidMode(containerPrefix + c.ID)
 		}
 	}
 	if hostConfig.IpcMode.IsContainer() {
 		ipcContainer := hostConfig.IpcMode.Container()
 		if c, err := daemon.GetContainer(ipcContainer); err == nil {
 			hostConfig.IpcMode = containertypes.IpcMode(containerPrefix + c.ID)
 		}
 	}
 	if hostConfig.NetworkMode.IsContainer() {
 		netContainer := hostConfig.NetworkMode.ConnectedContainer()
 		if c, err := daemon.GetContainer(netContainer); err == nil {
 			hostConfig.NetworkMode = containertypes.NetworkMode(containerPrefix + c.ID)
 		}
 	}
 }
 
b6e373c5
 // verifyPlatformContainerResources performs platform-specific validation of the container's resource-configuration
 func verifyPlatformContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo, update bool) (warnings []string, err error) {
9d87e6e0
 	fixMemorySwappiness(resources)
8fb0ca2c
 
4177b0ba
 	// memory subsystem checks and adjustments
c6bfb54a
 	if resources.Memory != 0 && resources.Memory < linuxMinMemory {
8fb0ca2c
 		return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB")
 	}
c6bfb54a
 	if resources.Memory > 0 && !sysInfo.MemoryLimit {
846f33f9
 		warnings = append(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
c6bfb54a
 		resources.Memory = 0
 		resources.MemorySwap = -1
8fb0ca2c
 	}
c6bfb54a
 	if resources.Memory > 0 && resources.MemorySwap != -1 && !sysInfo.SwapLimit {
846f33f9
 		warnings = append(warnings, "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.")
c6bfb54a
 		resources.MemorySwap = -1
8fb0ca2c
 	}
c6bfb54a
 	if resources.Memory > 0 && resources.MemorySwap > 0 && resources.MemorySwap < resources.Memory {
aae4bcf7
 		return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage")
8fb0ca2c
 	}
8ae6f6ac
 	if resources.Memory == 0 && resources.MemorySwap > 0 && !update {
aae4bcf7
 		return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage")
8fb0ca2c
 	}
9d87e6e0
 	if resources.MemorySwappiness != nil && !sysInfo.MemorySwappiness {
846f33f9
 		warnings = append(warnings, "Your kernel does not support memory swappiness capabilities or the cgroup is not mounted. Memory swappiness discarded.")
c6bfb54a
 		resources.MemorySwappiness = nil
19c7b65e
 	}
c6bfb54a
 	if resources.MemorySwappiness != nil {
 		swappiness := *resources.MemorySwappiness
9d87e6e0
 		if swappiness < 0 || swappiness > 100 {
aae4bcf7
 			return warnings, fmt.Errorf("Invalid value: %v, valid memory swappiness range is 0-100", swappiness)
4e25d298
 		}
19c7b65e
 	}
c6bfb54a
 	if resources.MemoryReservation > 0 && !sysInfo.MemoryReservation {
846f33f9
 		warnings = append(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.")
c6bfb54a
 		resources.MemoryReservation = 0
aa178099
 	}
50a61810
 	if resources.MemoryReservation > 0 && resources.MemoryReservation < linuxMinMemory {
 		return warnings, fmt.Errorf("Minimum memory reservation allowed is 4MB")
 	}
c6bfb54a
 	if resources.Memory > 0 && resources.MemoryReservation > 0 && resources.Memory < resources.MemoryReservation {
e5bed175
 		return warnings, fmt.Errorf("Minimum memory limit can not be less than memory reservation limit, see usage")
aa178099
 	}
c6bfb54a
 	if resources.KernelMemory > 0 && !sysInfo.KernelMemory {
846f33f9
 		warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
c6bfb54a
 		resources.KernelMemory = 0
b6f1b4ad
 	}
c6bfb54a
 	if resources.KernelMemory > 0 && resources.KernelMemory < linuxMinMemory {
2347f980
 		return warnings, fmt.Errorf("Minimum kernel memory limit allowed is 4MB")
 	}
da5d66fb
 	if resources.KernelMemory > 0 && !kernel.CheckKernelVersion(4, 0, 0) {
b6f1b4ad
 		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.")
 	}
f4a68733
 	if resources.OomKillDisable != nil && !sysInfo.OomKillDisable {
5a707d94
 		// only produce warnings if the setting wasn't to *disable* the OOM Kill; no point
 		// warning the caller if they already wanted the feature to be off
 		if *resources.OomKillDisable {
e5bed175
 			warnings = append(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.")
5a707d94
 		}
f4a68733
 		resources.OomKillDisable = nil
8498ed73
 	}
57f1305e
 	if resources.OomKillDisable != nil && *resources.OomKillDisable && resources.Memory == 0 {
 		warnings = append(warnings, "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.")
 	}
ffa1728d
 	if resources.PidsLimit != nil && !sysInfo.PidsLimit {
 		if *resources.PidsLimit > 0 {
 			warnings = append(warnings, "Your kernel does not support PIDs limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
 		}
 		resources.PidsLimit = nil
69cf0370
 	}
 
c6bfb54a
 	// cpu subsystem checks and adjustments
846baf1f
 	if resources.NanoCPUs > 0 && resources.CPUPeriod > 0 {
 		return warnings, fmt.Errorf("Conflicting options: Nano CPUs and CPU Period cannot both be set")
 	}
 	if resources.NanoCPUs > 0 && resources.CPUQuota > 0 {
 		return warnings, fmt.Errorf("Conflicting options: Nano CPUs and CPU Quota cannot both be set")
 	}
 	if resources.NanoCPUs > 0 && (!sysInfo.CPUCfsPeriod || !sysInfo.CPUCfsQuota) {
 		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")
 	}
d22ac2f3
 	// The highest precision we could get on Linux is 0.001, by setting
 	//   cpu.cfs_period_us=1000ms
 	//   cpu.cfs_quota=1ms
 	// See the following link for details:
 	// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
 	// Here we don't set the lower limit and it is up to the underlying platform (e.g., Linux) to return an error.
 	// The error message is 0.01 so that this is consistent with Windows
846baf1f
 	if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
d22ac2f3
 		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())
846baf1f
 	}
 
c6bfb54a
 	if resources.CPUShares > 0 && !sysInfo.CPUShares {
846f33f9
 		warnings = append(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.")
c6bfb54a
 		resources.CPUShares = 0
b7599d58
 	}
c6bfb54a
 	if resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod {
846f33f9
 		warnings = append(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.")
c6bfb54a
 		resources.CPUPeriod = 0
8fb0ca2c
 	}
62cb06a6
 	if resources.CPUPeriod != 0 && (resources.CPUPeriod < 1000 || resources.CPUPeriod > 1000000) {
b041fdc0
 		return warnings, fmt.Errorf("CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
 	}
c6bfb54a
 	if resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota {
846f33f9
 		warnings = append(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
c6bfb54a
 		resources.CPUQuota = 0
8fb0ca2c
 	}
b041fdc0
 	if resources.CPUQuota > 0 && resources.CPUQuota < 1000 {
 		return warnings, fmt.Errorf("CPU cfs quota can not be less than 1ms (i.e. 1000)")
 	}
ea8c6908
 	if resources.CPUPercent > 0 {
40f25809
 		warnings = append(warnings, fmt.Sprintf("%s does not support CPU percent. Percent discarded.", runtime.GOOS))
ea8c6908
 		resources.CPUPercent = 0
 	}
c6bfb54a
 
 	// cpuset subsystem checks and adjustments
 	if (resources.CpusetCpus != "" || resources.CpusetMems != "") && !sysInfo.Cpuset {
846f33f9
 		warnings = append(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. Cpuset discarded.")
c6bfb54a
 		resources.CpusetCpus = ""
 		resources.CpusetMems = ""
b7599d58
 	}
c6bfb54a
 	cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(resources.CpusetCpus)
94464e3a
 	if err != nil {
f8e876d7
 		return warnings, errors.Wrapf(err, "Invalid value %s for cpuset cpus", resources.CpusetCpus)
94464e3a
 	}
 	if !cpusAvailable {
aae4bcf7
 		return warnings, fmt.Errorf("Requested CPUs are not available - requested %s, available: %s", resources.CpusetCpus, sysInfo.Cpus)
94464e3a
 	}
c6bfb54a
 	memsAvailable, err := sysInfo.IsCpusetMemsAvailable(resources.CpusetMems)
94464e3a
 	if err != nil {
f8e876d7
 		return warnings, errors.Wrapf(err, "Invalid value %s for cpuset mems", resources.CpusetMems)
94464e3a
 	}
 	if !memsAvailable {
aae4bcf7
 		return warnings, fmt.Errorf("Requested memory nodes are not available - requested %s, available: %s", resources.CpusetMems, sysInfo.Mems)
94464e3a
 	}
c6bfb54a
 
 	// blkio subsystem checks and adjustments
 	if resources.BlkioWeight > 0 && !sysInfo.BlkioWeight {
846f33f9
 		warnings = append(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.")
c6bfb54a
 		resources.BlkioWeight = 0
b7599d58
 	}
c6bfb54a
 	if resources.BlkioWeight > 0 && (resources.BlkioWeight < 10 || resources.BlkioWeight > 1000) {
aae4bcf7
 		return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000")
8fb0ca2c
 	}
8df20663
 	if resources.IOMaximumBandwidth != 0 || resources.IOMaximumIOps != 0 {
 		return warnings, fmt.Errorf("Invalid QoS settings: %s does not support Maximum IO Bandwidth or Maximum IO IOps", runtime.GOOS)
 	}
c6bfb54a
 	if len(resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
846f33f9
 		warnings = append(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.")
c6bfb54a
 		resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{}
0fbfa144
 	}
c6bfb54a
 	if len(resources.BlkioDeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice {
846f33f9
 		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.")
c6bfb54a
 		resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{}
3f15a055
 	}
c6bfb54a
 	if len(resources.BlkioDeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice {
846f33f9
 		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.")
c6bfb54a
 		resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{}
ebcb7d6b
 
3f15a055
 	}
843084b0
 	if len(resources.BlkioDeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice {
846f33f9
 		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.")
843084b0
 		resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{}
 	}
 	if len(resources.BlkioDeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice {
846f33f9
 		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.")
843084b0
 		resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{}
 	}
c6bfb54a
 
 	return warnings, nil
 }
 
ca89c329
 func (daemon *Daemon) getCgroupDriver() string {
57b59f87
 	if daemon.Rootless() {
 		return cgroupNoneDriver
 	}
8af4f89c
 	cgroupDriver := cgroupFsDriver
ca89c329
 
7ed3d265
 	if UsingSystemd(daemon.configStore) {
 		cgroupDriver = cgroupSystemdDriver
 	}
 	return cgroupDriver
 }
 
 // getCD gets the raw value of the native.cgroupdriver option, if set.
db63f937
 func getCD(config *config.Config) string {
7ed3d265
 	for _, option := range config.ExecOptions {
5ce5a8e9
 		key, val, err := parsers.ParseKeyValueOpt(option)
 		if err != nil || !strings.EqualFold(key, "native.cgroupdriver") {
 			continue
 		}
7ed3d265
 		return val
5ce5a8e9
 	}
7ed3d265
 	return ""
8af4f89c
 }
 
7ed3d265
 // VerifyCgroupDriver validates native.cgroupdriver
db63f937
 func VerifyCgroupDriver(config *config.Config) error {
7ed3d265
 	cd := getCD(config)
 	if cd == "" || cd == cgroupFsDriver || cd == cgroupSystemdDriver {
 		return nil
 	}
57b59f87
 	if cd == cgroupNoneDriver {
 		return fmt.Errorf("native.cgroupdriver option %s is internally used and cannot be specified manually", cd)
 	}
7ed3d265
 	return fmt.Errorf("native.cgroupdriver option %s not supported", cd)
5ce5a8e9
 }
 
7ed3d265
 // UsingSystemd returns true if cli option includes native.cgroupdriver=systemd
db63f937
 func UsingSystemd(config *config.Config) bool {
7ed3d265
 	return getCD(config) == cgroupSystemdDriver
5ce5a8e9
 }
 
c6bfb54a
 // verifyPlatformContainerSettings performs platform-specific validation of the
 // hostconfig and config structures.
e2786787
 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) {
f6002117
 	if hostConfig == nil {
 		return nil, nil
 	}
c6bfb54a
 	sysInfo := sysinfo.New(true)
 
b6e373c5
 	w, err := verifyPlatformContainerResources(&hostConfig.Resources, sysInfo, update)
28b291df
 
 	// no matter err is nil or not, w could have data in itself.
 	warnings = append(warnings, w...)
 
c6bfb54a
 	if err != nil {
 		return warnings, err
 	}
 
5190794f
 	if hostConfig.ShmSize < 0 {
e5bed175
 		return warnings, fmt.Errorf("SHM size can not be less than 0")
c6bfb54a
 	}
 
d3af7f28
 	if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 {
aae4bcf7
 		return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj)
d3af7f28
 	}
5fb7f9b2
 
27f34593
 	// ip-forwarding does not affect container with '--net=host' (or '--net=none')
 	if sysInfo.IPv4ForwardingDisabled && !(hostConfig.NetworkMode.IsHost() || hostConfig.NetworkMode.IsNone()) {
8fb0ca2c
 		warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.")
 	}
2e23ef53
 	if hostConfig.NetworkMode.IsHost() && len(hostConfig.PortBindings) > 0 {
 		warnings = append(warnings, "Published ports are discarded when using host network mode")
 	}
 
d5743a3a
 	// check for various conflicting options with user namespaces
6993e891
 	if daemon.configStore.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() {
d5743a3a
 		if hostConfig.Privileged {
9b47b7b1
 			return warnings, fmt.Errorf("privileged mode is incompatible with user namespaces.  You must run the container in the host namespace when running privileged mode")
d5743a3a
 		}
3c3d2bf8
 		if hostConfig.NetworkMode.IsHost() && !hostConfig.UsernsMode.IsHost() {
9b47b7b1
 			return warnings, fmt.Errorf("cannot share the host's network namespace when user namespaces are enabled")
d5743a3a
 		}
3c3d2bf8
 		if hostConfig.PidMode.IsHost() && !hostConfig.UsernsMode.IsHost() {
9b47b7b1
 			return warnings, fmt.Errorf("cannot share the host PID namespace when user namespaces are enabled")
d5743a3a
 		}
557c7cb8
 	}
7ed3d265
 	if hostConfig.CgroupParent != "" && UsingSystemd(daemon.configStore) {
5ce5a8e9
 		// CgroupParent for systemd cgroup should be named as "xxx.slice"
 		if len(hostConfig.CgroupParent) <= 6 || !strings.HasSuffix(hostConfig.CgroupParent, ".slice") {
 			return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
 		}
 	}
7b2e5216
 	if hostConfig.Runtime == "" {
 		hostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
 	}
 
 	if rt := daemon.configStore.GetRuntime(hostConfig.Runtime); rt == nil {
 		return warnings, fmt.Errorf("Unknown runtime specified %s", hostConfig.Runtime)
 	}
 
6a70fd22
 	parser := volumemounts.NewParser(runtime.GOOS)
4a8799dc
 	for dest := range hostConfig.Tmpfs {
e89b6e8c
 		if err := parser.ValidateTmpfsMountDestination(dest); err != nil {
4a8799dc
 			return warnings, err
 		}
 	}
 
8fb0ca2c
 	return warnings, nil
 }
 
ddae20c0
 func (daemon *Daemon) loadRuntimes() error {
 	return daemon.initRuntimes(daemon.configStore.Runtimes)
 }
 
 func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) {
 	runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes")
 	// Remove old temp directory if any
 	os.RemoveAll(runtimeDir + "-old")
 	tmpDir, err := ioutils.TempDir(daemon.configStore.Root, "gen-runtimes")
 	if err != nil {
3737194b
 		return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
ddae20c0
 	}
 	defer func() {
 		if err != nil {
 			if err1 := os.RemoveAll(tmpDir); err1 != nil {
 				logrus.WithError(err1).WithField("dir", tmpDir).
3737194b
 					Warn("failed to remove tmp dir")
ddae20c0
 			}
 			return
 		}
 
 		if err = os.Rename(runtimeDir, runtimeDir+"-old"); err != nil {
 			return
 		}
 		if err = os.Rename(tmpDir, runtimeDir); err != nil {
3737194b
 			err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start")
ddae20c0
 			return
 		}
 		if err = os.RemoveAll(runtimeDir + "-old"); err != nil {
 			logrus.WithError(err).WithField("dir", tmpDir).
3737194b
 				Warn("failed to remove old runtimes dir")
ddae20c0
 		}
 	}()
 
 	for name, rt := range runtimes {
 		if len(rt.Args) == 0 {
 			continue
 		}
 
 		script := filepath.Join(tmpDir, name)
 		content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " "))
 		if err := ioutil.WriteFile(script, []byte(content), 0700); err != nil {
 			return err
 		}
 	}
 	return nil
 }
 
5ce5a8e9
 // verifyDaemonSettings performs validation of daemon config struct
db63f937
 func verifyDaemonSettings(conf *config.Config) error {
34418110
 	if conf.ContainerdNamespace == conf.ContainerdPluginNamespace {
 		return errors.New("containers namespace and plugins namespace cannot be the same")
 	}
8fb0ca2c
 	// Check for mutually incompatible config options
db63f937
 	if conf.BridgeConfig.Iface != "" && conf.BridgeConfig.IP != "" {
858f852d
 		return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one")
8fb0ca2c
 	}
db63f937
 	if !conf.BridgeConfig.EnableIPTables && !conf.BridgeConfig.InterContainerCommunication {
858f852d
 		return fmt.Errorf("You specified --iptables=false with --icc=false. ICC=false uses iptables to function. Please set --icc or --iptables to true")
8fb0ca2c
 	}
db63f937
 	if !conf.BridgeConfig.EnableIPTables && conf.BridgeConfig.EnableIPMasq {
 		conf.BridgeConfig.EnableIPMasq = false
8fb0ca2c
 	}
db63f937
 	if err := VerifyCgroupDriver(conf); err != nil {
7ed3d265
 		return err
 	}
db63f937
 	if conf.CgroupParent != "" && UsingSystemd(conf) {
 		if len(conf.CgroupParent) <= 6 || !strings.HasSuffix(conf.CgroupParent, ".slice") {
5ce5a8e9
 			return fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
 		}
 	}
7b2e5216
 
db63f937
 	if conf.DefaultRuntime == "" {
 		conf.DefaultRuntime = config.StockRuntimeName
7b2e5216
 	}
db63f937
 	if conf.Runtimes == nil {
 		conf.Runtimes = make(map[string]types.Runtime)
7b2e5216
 	}
ddae20c0
 	conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: DefaultRuntimeName}
7b2e5216
 
8fb0ca2c
 	return nil
 }
 
62a75fca
 // checkSystem validates platform-specific requirements
8fb0ca2c
 func checkSystem() error {
94464e3a
 	return checkKernel()
8fb0ca2c
 }
 
140a7434
 // configureMaxThreads sets the Go runtime max threads threshold
 // which is 90% of the kernel setting from /proc/sys/kernel/threads-max
db63f937
 func configureMaxThreads(config *config.Config) error {
140a7434
 	mt, err := ioutil.ReadFile("/proc/sys/kernel/threads-max")
 	if err != nil {
 		return err
 	}
 	mtint, err := strconv.Atoi(strings.TrimSpace(string(mt)))
 	if err != nil {
 		return err
 	}
 	maxThreads := (mtint / 100) * 90
 	debug.SetMaxThreads(maxThreads)
 	logrus.Debugf("Golang's threads limit set to %d", maxThreads)
 	return nil
 }
 
885b29df
 func overlaySupportsSelinux() (bool, error) {
 	f, err := os.Open("/proc/kallsyms")
 	if err != nil {
 		if os.IsNotExist(err) {
 			return false, nil
 		}
 		return false, err
 	}
 	defer f.Close()
 
 	var symAddr, symType, symName, text string
 
 	s := bufio.NewScanner(f)
 	for s.Scan() {
 		if err := s.Err(); err != nil {
 			return false, err
 		}
 
 		text = s.Text()
 		if _, err := fmt.Sscanf(text, "%s %s %s", &symAddr, &symType, &symName); err != nil {
 			return false, fmt.Errorf("Scanning '%s' failed: %s", text, err)
 		}
 
 		// Check for presence of symbol security_inode_copy_up.
 		if symName == "security_inode_copy_up" {
 			return true, nil
 		}
 	}
 	return false, nil
 }
 
25c9bd81
 // configureKernelSecuritySupport configures and validates security support for the kernel
ce8e529e
 func configureKernelSecuritySupport(config *config.Config, driverName string) error {
8fb0ca2c
 	if config.EnableSelinuxSupport {
b71cd179
 		if !selinuxEnabled() {
8fb0ca2c
 			logrus.Warn("Docker could not enable SELinux on the host system")
885b29df
 			return nil
 		}
 
ce8e529e
 		if driverName == "overlay" || driverName == "overlay2" {
885b29df
 			// If driver is overlay or overlay2, make sure kernel
 			// supports selinux with overlay.
 			supported, err := overlaySupportsSelinux()
 			if err != nil {
 				return err
 			}
 
 			if !supported {
ce8e529e
 				logrus.Warnf("SELinux is not supported with the %v graph driver on this kernel", driverName)
885b29df
 			}
8fb0ca2c
 		}
 	} else {
 		selinuxSetDisabled()
 	}
 	return nil
 }
 
db63f937
 func (daemon *Daemon) initNetworkController(config *config.Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) {
c5393ee1
 	netOptions, err := daemon.networkOptions(config, daemon.PluginStore, activeSandboxes)
da5a3e6d
 	if err != nil {
 		return nil, err
 	}
 
 	controller, err := libnetwork.New(netOptions...)
8fb0ca2c
 	if err != nil {
 		return nil, fmt.Errorf("error obtaining controller instance: %v", err)
 	}
 
ecffb6d5
 	if len(activeSandboxes) > 0 {
fa710e50
 		logrus.Info("There are old running containers, the network config will not take affect")
ecffb6d5
 		return controller, nil
 	}
 
8fb0ca2c
 	// Initialize default network on "null"
ecffb6d5
 	if n, _ := controller.NetworkByName("none"); n == nil {
 		if _, err := controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(true)); err != nil {
 			return nil, fmt.Errorf("Error creating default \"null\" network: %v", err)
 		}
8fb0ca2c
 	}
 
 	// Initialize default network on "host"
ecffb6d5
 	if n, _ := controller.NetworkByName("host"); n == nil {
 		if _, err := controller.NewNetwork("host", "host", "", libnetwork.NetworkOptionPersist(true)); err != nil {
 			return nil, fmt.Errorf("Error creating default \"host\" network: %v", err)
 		}
8fb0ca2c
 	}
a0af884d
 
 	// Clear stale bridge network
 	if n, err := controller.NetworkByName("bridge"); err == nil {
 		if err = n.Delete(); err != nil {
 			return nil, fmt.Errorf("could not delete the default bridge network: %v", err)
173b3c36
 		}
 		if len(config.NetworkConfig.DefaultAddressPools.Value()) > 0 && !daemon.configStore.LiveRestoreEnabled {
 			removeDefaultBridgeInterface()
a0af884d
 		}
 	}
 
c9328c6c
 	if !config.DisableBridge {
 		// Initialize default driver "bridge"
 		if err := initBridgeDriver(controller, config); err != nil {
 			return nil, err
 		}
a0af884d
 	} else {
 		removeDefaultBridgeInterface()
c9328c6c
 	}
 
 	return controller, nil
 }
 
db63f937
 func driverOptions(config *config.Config) []nwconfig.Option {
6db15920
 	bridgeConfig := options.Generic{
db63f937
 		"EnableIPForwarding":  config.BridgeConfig.EnableIPForward,
 		"EnableIPTables":      config.BridgeConfig.EnableIPTables,
 		"EnableUserlandProxy": config.BridgeConfig.EnableUserlandProxy,
 		"UserlandProxyPath":   config.BridgeConfig.UserlandProxyPath}
6db15920
 	bridgeOption := options.Generic{netlabel.GenericData: bridgeConfig}
8fb0ca2c
 
6db15920
 	dOptions := []nwconfig.Option{}
 	dOptions = append(dOptions, nwconfig.OptionDriverConfig("bridge", bridgeOption))
 	return dOptions
 }
8fb0ca2c
 
db63f937
 func initBridgeDriver(controller libnetwork.NetworkController, config *config.Config) error {
0f351ce3
 	bridgeName := bridge.DefaultBridgeName
db63f937
 	if config.BridgeConfig.Iface != "" {
 		bridgeName = config.BridgeConfig.Iface
0f351ce3
 	}
 	netOption := map[string]string{
 		bridge.BridgeName:         bridgeName,
 		bridge.DefaultBridge:      strconv.FormatBool(true),
 		netlabel.DriverMTU:        strconv.Itoa(config.Mtu),
db63f937
 		bridge.EnableIPMasquerade: strconv.FormatBool(config.BridgeConfig.EnableIPMasq),
 		bridge.EnableICC:          strconv.FormatBool(config.BridgeConfig.InterContainerCommunication),
0f351ce3
 	}
 
 	// --ip processing
db63f937
 	if config.BridgeConfig.DefaultIP != nil {
 		netOption[bridge.DefaultBindingIP] = config.BridgeConfig.DefaultIP.String()
0f351ce3
 	}
 
cfa3682c
 	var (
 		ipamV4Conf *libnetwork.IpamConf
 		ipamV6Conf *libnetwork.IpamConf
 	)
0f351ce3
 
cfa3682c
 	ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
0f351ce3
 
e9c4c513
 	nwList, nw6List, err := netutils.ElectInterfaceAddresses(bridgeName)
 	if err != nil {
 		return errors.Wrap(err, "list bridge addresses failed")
 	}
 
 	nw := nwList[0]
db63f937
 	if len(nwList) > 1 && config.BridgeConfig.FixedCIDR != "" {
 		_, fCIDR, err := net.ParseCIDR(config.BridgeConfig.FixedCIDR)
e9c4c513
 		if err != nil {
 			return errors.Wrap(err, "parse CIDR failed")
0f351ce3
 		}
e9c4c513
 		// Iterate through in case there are multiple addresses for the bridge
 		for _, entry := range nwList {
 			if fCIDR.Contains(entry.IP) {
 				nw = entry
 				break
 			}
 		}
 	}
 
 	ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String()
 	hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask)
 	if hip.IsGlobalUnicast() {
 		ipamV4Conf.Gateway = nw.IP.String()
8fb0ca2c
 	}
 
db63f937
 	if config.BridgeConfig.IP != "" {
 		ipamV4Conf.PreferredPool = config.BridgeConfig.IP
 		ip, _, err := net.ParseCIDR(config.BridgeConfig.IP)
8fb0ca2c
 		if err != nil {
c9328c6c
 			return err
8fb0ca2c
 		}
0f351ce3
 		ipamV4Conf.Gateway = ip.String()
126d1b6c
 	} else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" {
 		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)
8fb0ca2c
 	}
 
db63f937
 	if config.BridgeConfig.FixedCIDR != "" {
 		_, fCIDR, err := net.ParseCIDR(config.BridgeConfig.FixedCIDR)
8fb0ca2c
 		if err != nil {
c9328c6c
 			return err
8fb0ca2c
 		}
 
0f351ce3
 		ipamV4Conf.SubPool = fCIDR.String()
8fb0ca2c
 	}
 
db63f937
 	if config.BridgeConfig.DefaultGatewayIPv4 != nil {
 		ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.BridgeConfig.DefaultGatewayIPv4.String()
0f351ce3
 	}
 
cfa3682c
 	var deferIPv6Alloc bool
db63f937
 	if config.BridgeConfig.FixedCIDRv6 != "" {
 		_, fCIDRv6, err := net.ParseCIDR(config.BridgeConfig.FixedCIDRv6)
8fb0ca2c
 		if err != nil {
c9328c6c
 			return err
8fb0ca2c
 		}
095a8ac5
 
 		// In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has
 		// at least 48 host bits, we need to guarantee the current behavior where the containers'
 		// IPv6 addresses will be constructed based on the containers' interface MAC address.
 		// We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints
 		// on this network until after the driver has created the endpoint and returned the
 		// constructed address. Libnetwork will then reserve this address with the ipam driver.
 		ones, _ := fCIDRv6.Mask.Size()
 		deferIPv6Alloc = ones <= 80
 
0f351ce3
 		if ipamV6Conf == nil {
aa97eee1
 			ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
0f351ce3
 		}
 		ipamV6Conf.PreferredPool = fCIDRv6.String()
cfa3682c
 
 		// In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6
 		// address belongs to the same network, we need to inform libnetwork about it, so
 		// that it can be reserved with IPAM and it will not be given away to somebody else
 		for _, nw6 := range nw6List {
 			if fCIDRv6.Contains(nw6.IP) {
 				ipamV6Conf.Gateway = nw6.IP.String()
 				break
 			}
 		}
8fb0ca2c
 	}
 
db63f937
 	if config.BridgeConfig.DefaultGatewayIPv6 != nil {
0f351ce3
 		if ipamV6Conf == nil {
aa97eee1
 			ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
0f351ce3
 		}
db63f937
 		ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = config.BridgeConfig.DefaultGatewayIPv6.String()
8fb0ca2c
 	}
 
cfa3682c
 	v4Conf := []*libnetwork.IpamConf{ipamV4Conf}
0f351ce3
 	v6Conf := []*libnetwork.IpamConf{}
 	if ipamV6Conf != nil {
 		v6Conf = append(v6Conf, ipamV6Conf)
8fb0ca2c
 	}
 	// Initialize default network on "bridge" with the same name
6eb2b903
 	_, err = controller.NewNetwork("bridge", "bridge", "",
db63f937
 		libnetwork.NetworkOptionEnableIPv6(config.BridgeConfig.EnableIPv6),
dfb00652
 		libnetwork.NetworkOptionDriverOpts(netOption),
64a6dc35
 		libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil),
095a8ac5
 		libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
8fb0ca2c
 	if err != nil {
c9328c6c
 		return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
8fb0ca2c
 	}
c9328c6c
 	return nil
8fb0ca2c
 }
b7f887a9
 
a0af884d
 // Remove default bridge interface if present (--bridge=none use case)
 func removeDefaultBridgeInterface() {
 	if lnk, err := netlink.LinkByName(bridge.DefaultBridgeName); err == nil {
 		if err := netlink.LinkDel(lnk); err != nil {
 			logrus.Warnf("Failed to remove bridge interface (%s): %v", bridge.DefaultBridgeName, err)
 		}
 	}
 }
 
763d8392
 func setupInitLayer(idMapping *idtools.IdentityMapping) func(containerfs.ContainerFS) error {
c502bcff
 	return func(initPath containerfs.ContainerFS) error {
763d8392
 		return initlayer.Setup(initPath, idMapping.RootPair())
c502bcff
 	}
2508ca00
 }
 
557c7cb8
 // Parse the remapped root (user namespace) option, which can be one of:
 //   username            - valid username from /etc/passwd
 //   username:groupname  - valid username; valid groupname from /etc/group
 //   uid                 - 32-bit unsigned int valid Linux UID value
 //   uid:gid             - uid value; 32-bit unsigned int Linux GID value
 //
 //  If no groupname is specified, and a username is specified, an attempt
 //  will be made to lookup a gid for that username as a groupname
 //
 //  If names are used, they are verified to exist in passwd/group
 func parseRemappedRoot(usergrp string) (string, string, error) {
 
 	var (
 		userID, groupID     int
 		username, groupname string
 	)
 
 	idparts := strings.Split(usergrp, ":")
 	if len(idparts) > 2 {
 		return "", "", fmt.Errorf("Invalid user/group specification in --userns-remap: %q", usergrp)
 	}
 
 	if uid, err := strconv.ParseInt(idparts[0], 10, 32); err == nil {
 		// must be a uid; take it as valid
 		userID = int(uid)
6cb8392b
 		luser, err := idtools.LookupUID(userID)
557c7cb8
 		if err != nil {
 			return "", "", fmt.Errorf("Uid %d has no entry in /etc/passwd: %v", userID, err)
 		}
 		username = luser.Name
 		if len(idparts) == 1 {
 			// if the uid was numeric and no gid was specified, take the uid as the gid
 			groupID = userID
6cb8392b
 			lgrp, err := idtools.LookupGID(groupID)
557c7cb8
 			if err != nil {
 				return "", "", fmt.Errorf("Gid %d has no entry in /etc/group: %v", groupID, err)
 			}
 			groupname = lgrp.Name
 		}
 	} else {
 		lookupName := idparts[0]
 		// special case: if the user specified "default", they want Docker to create or
 		// use (after creation) the "dockremap" user/group for root remapping
 		if lookupName == defaultIDSpecifier {
 			lookupName = defaultRemappedID
 		}
6cb8392b
 		luser, err := idtools.LookupUser(lookupName)
557c7cb8
 		if err != nil && idparts[0] != defaultIDSpecifier {
 			// error if the name requested isn't the special "dockremap" ID
 			return "", "", fmt.Errorf("Error during uid lookup for %q: %v", lookupName, err)
 		} else if err != nil {
 			// special case-- if the username == "default", then we have been asked
 			// to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid}
 			// ranges will be used for the user and group mappings in user namespaced containers
 			_, _, err := idtools.AddNamespaceRangesUser(defaultRemappedID)
 			if err == nil {
 				return defaultRemappedID, defaultRemappedID, nil
 			}
 			return "", "", fmt.Errorf("Error during %q user creation: %v", defaultRemappedID, err)
 		}
 		username = luser.Name
 		if len(idparts) == 1 {
 			// we only have a string username, and no group specified; look up gid from username as group
6cb8392b
 			group, err := idtools.LookupGroup(lookupName)
557c7cb8
 			if err != nil {
 				return "", "", fmt.Errorf("Error during gid lookup for %q: %v", lookupName, err)
 			}
 			groupname = group.Name
 		}
 	}
 
 	if len(idparts) == 2 {
 		// groupname or gid is separately specified and must be resolved
25c9bd81
 		// to an unsigned 32-bit gid
557c7cb8
 		if gid, err := strconv.ParseInt(idparts[1], 10, 32); err == nil {
 			// must be a gid, take it as valid
 			groupID = int(gid)
6cb8392b
 			lgrp, err := idtools.LookupGID(groupID)
557c7cb8
 			if err != nil {
 				return "", "", fmt.Errorf("Gid %d has no entry in /etc/passwd: %v", groupID, err)
 			}
 			groupname = lgrp.Name
 		} else {
 			// not a number; attempt a lookup
6cb8392b
 			if _, err := idtools.LookupGroup(idparts[1]); err != nil {
0e025b4b
 				return "", "", fmt.Errorf("Error during groupname lookup for %q: %v", idparts[1], err)
557c7cb8
 			}
 			groupname = idparts[1]
 		}
 	}
 	return username, groupname, nil
 }
 
763d8392
 func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) {
557c7cb8
 	if runtime.GOOS != "linux" && config.RemappedRoot != "" {
09cd96c5
 		return nil, fmt.Errorf("User namespaces are only supported on Linux")
557c7cb8
 	}
 
 	// if the daemon was started with remapped root option, parse
 	// the config option to the int uid,gid values
 	if config.RemappedRoot != "" {
 		username, groupname, err := parseRemappedRoot(config.RemappedRoot)
 		if err != nil {
09cd96c5
 			return nil, err
557c7cb8
 		}
 		if username == "root" {
 			// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
 			// effectively
44ccbb31
 			logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
763d8392
 			return &idtools.IdentityMapping{}, nil
557c7cb8
 		}
 		logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname)
 		// update remapped root setting now that we have resolved them to actual names
 		config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname)
 
763d8392
 		mappings, err := idtools.NewIdentityMapping(username, groupname)
557c7cb8
 		if err != nil {
3737194b
 			return nil, errors.Wrap(err, "Can't create ID mappings")
557c7cb8
 		}
09cd96c5
 		return mappings, nil
557c7cb8
 	}
763d8392
 	return &idtools.IdentityMapping{}, nil
557c7cb8
 }
 
763d8392
 func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error {
557c7cb8
 	config.Root = rootDir
e91ca0e2
 	// the docker root metadata directory needs to have execute permissions for all users (g+x,o+x)
557c7cb8
 	// so that syscalls executing as non-root, operating on subdirectories of the graph root
 	// (e.g. mounted layers of a container) can traverse this path.
 	// The user namespace support will create subdirectories for the remapped root host uid:gid
 	// pair owned by that same uid:gid pair for proper write access to those needed metadata and
 	// layer content subtrees.
 	if _, err := os.Stat(rootDir); err == nil {
 		// root current exists; verify the access bits are correct by setting them
e91ca0e2
 		if err = os.Chmod(rootDir, 0711); err != nil {
557c7cb8
 			return err
 		}
 	} else if os.IsNotExist(err) {
e91ca0e2
 		// no root exists yet, create it 0711 with root:root ownership
 		if err := os.MkdirAll(rootDir, 0711); err != nil {
557c7cb8
 			return err
 		}
 	}
 
 	// if user namespaces are enabled we will create a subtree underneath the specified root
 	// with any/all specified remapped root uid/gid options on the daemon creating
 	// a new subdirectory with ownership set to the remapped uid/gid (so as to allow
 	// `chdir()` to work for containers namespaced to that uid/gid)
 	if config.RemappedRoot != "" {
763d8392
 		config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIdentity.UID, rootIdentity.GID))
557c7cb8
 		logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
25c9bd81
 		// Create the root directory if it doesn't exist
763d8392
 		if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIdentity); err != nil {
557c7cb8
 			return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err)
 		}
43a1df6b
 		// we also need to verify that any pre-existing directories in the path to
 		// the graphroot won't block access to remapped root--if any pre-existing directory
 		// has strict permissions that don't allow "x", container start will fail, so
 		// better to warn and fail now
 		dirPath := config.Root
 		for {
 			dirPath = filepath.Dir(dirPath)
 			if dirPath == "/" {
 				break
 			}
763d8392
 			if !idtools.CanAccess(dirPath, rootIdentity) {
9b47b7b1
 				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)
43a1df6b
 			}
 		}
557c7cb8
 	}
a510192b
 
c403f003
 	if err := setupDaemonRootPropagation(config); err != nil {
 		logrus.WithError(err).WithField("dir", config.Root).Warn("Error while setting daemon root propagation, this is not generally critical but may cause some functionality to not work or fallback to less desirable behavior")
 	}
 	return nil
 }
 
 func setupDaemonRootPropagation(cfg *config.Config) error {
 	rootParentMount, options, err := getSourceMount(cfg.Root)
 	if err != nil {
 		return errors.Wrap(err, "error getting daemon root's parent mount")
 	}
 
 	var cleanupOldFile bool
 	cleanupFile := getUnmountOnShutdownPath(cfg)
 	defer func() {
 		if !cleanupOldFile {
 			return
a510192b
 		}
c403f003
 		if err := os.Remove(cleanupFile); err != nil && !os.IsNotExist(err) {
 			logrus.WithError(err).WithField("file", cleanupFile).Warn("could not clean up old root propagation unmount file")
 		}
 	}()
 
 	if hasMountinfoOption(options, sharedPropagationOption, slavePropagationOption) {
 		cleanupOldFile = true
 		return nil
 	}
 
 	if err := mount.MakeShared(cfg.Root); err != nil {
 		return errors.Wrap(err, "could not setup daemon root propagation to shared")
 	}
 
 	// check the case where this may have already been a mount to itself.
 	// If so then the daemon only performed a remount and should not try to unmount this later.
 	if rootParentMount == cfg.Root {
 		cleanupOldFile = true
 		return nil
 	}
 
c67edc5d
 	if err := os.MkdirAll(filepath.Dir(cleanupFile), 0700); err != nil {
 		return errors.Wrap(err, "error creating dir to store mount cleanup file")
 	}
 
c403f003
 	if err := ioutil.WriteFile(cleanupFile, nil, 0600); err != nil {
 		return errors.Wrap(err, "error writing file to signal mount cleanup on shutdown")
a510192b
 	}
557c7cb8
 	return nil
 }
 
c403f003
 // getUnmountOnShutdownPath generates the path to used when writing the file that signals to the daemon that on shutdown
 // the daemon root should be unmounted.
 func getUnmountOnShutdownPath(config *config.Config) string {
 	return filepath.Join(config.ExecRoot, "unmount-on-shutdown")
 }
 
abd72d40
 // registerLinks writes the links to a file.
7ac4232e
 func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
e221b8a3
 	if hostConfig == nil || hostConfig.NetworkMode.IsUserDefined() {
c5e6a4b3
 		return nil
 	}
 
 	for _, l := range hostConfig.Links {
c424be21
 		name, alias, err := opts.ParseLink(l)
c5e6a4b3
 		if err != nil {
 			return err
 		}
d7d512bb
 		child, err := daemon.GetContainer(name)
c5e6a4b3
 		if err != nil {
1e0234dd
 			if errdefs.IsNotFound(err) {
 				// Trying to link to a non-existing container is not valid, and
 				// should return an "invalid parameter" error. Returning a "not
 				// found" error here would make the client report the container's
 				// image could not be found (see moby/moby#39823)
 				err = errdefs.InvalidParameter(err)
 			}
ebcb7d6b
 			return errors.Wrapf(err, "could not get container for %s", name)
c5e6a4b3
 		}
6bb0d181
 		for child.HostConfig.NetworkMode.IsContainer() {
 			parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2)
d7d512bb
 			child, err = daemon.GetContainer(parts[1])
c5e6a4b3
 			if err != nil {
1e0234dd
 				if errdefs.IsNotFound(err) {
 					// Trying to link to a non-existing container is not valid, and
 					// should return an "invalid parameter" error. Returning a "not
 					// found" error here would make the client report the container's
 					// image could not be found (see moby/moby#39823)
 					err = errdefs.InvalidParameter(err)
 				}
ebcb7d6b
 				return errors.Wrapf(err, "Could not get container for %s", parts[1])
c5e6a4b3
 			}
 		}
6bb0d181
 		if child.HostConfig.NetworkMode.IsHost() {
c5e6a4b3
 			return runconfig.ErrConflictHostNetworkAndLinks
 		}
abd72d40
 		if err := daemon.registerLink(container, child, alias); err != nil {
c5e6a4b3
 			return err
 		}
 	}
 
 	// After we load all the links into the daemon
 	// set them to nil on the hostconfig
a43be343
 	_, err := container.WriteHostConfig()
 	return err
c5e6a4b3
 }
47c56e43
 
3a497650
 // conditionalMountOnStart is a platform specific helper function during the
 // container start to call mount.
6bb0d181
 func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
3a497650
 	return daemon.Mount(container)
 }
 
 // conditionalUnmountOnCleanup is a platform specific helper function called
 // during the cleanup of a container to unmount.
9c4570a9
 func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
 	return daemon.Unmount(container)
3a497650
 }
 
ddae20c0
 func copyBlkioEntry(entries []*containerd_cgroups.BlkIOEntry) []types.BlkioStatEntry {
 	out := make([]types.BlkioStatEntry, len(entries))
 	for i, re := range entries {
 		out[i] = types.BlkioStatEntry{
 			Major: re.Major,
 			Minor: re.Minor,
 			Op:    re.Op,
 			Value: re.Value,
 		}
 	}
 	return out
 }
 
9c4570a9
 func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
 	if !c.IsRunning() {
ebcb7d6b
 		return nil, errNotRunning(c.ID)
9c4570a9
 	}
ddae20c0
 	cs, err := daemon.containerd.Stats(context.Background(), c.ID)
9c4570a9
 	if err != nil {
4a6cbf9b
 		if strings.Contains(err.Error(), "container not found") {
ebcb7d6b
 			return nil, containerNotFound(c.ID)
4a6cbf9b
 		}
9c4570a9
 		return nil, err
 	}
 	s := &types.StatsJSON{}
ddae20c0
 	s.Read = cs.Read
 	stats := cs.Metrics
 	if stats.Blkio != nil {
9c4570a9
 		s.BlkioStats = types.BlkioStats{
ddae20c0
 			IoServiceBytesRecursive: copyBlkioEntry(stats.Blkio.IoServiceBytesRecursive),
 			IoServicedRecursive:     copyBlkioEntry(stats.Blkio.IoServicedRecursive),
 			IoQueuedRecursive:       copyBlkioEntry(stats.Blkio.IoQueuedRecursive),
 			IoServiceTimeRecursive:  copyBlkioEntry(stats.Blkio.IoServiceTimeRecursive),
 			IoWaitTimeRecursive:     copyBlkioEntry(stats.Blkio.IoWaitTimeRecursive),
 			IoMergedRecursive:       copyBlkioEntry(stats.Blkio.IoMergedRecursive),
 			IoTimeRecursive:         copyBlkioEntry(stats.Blkio.IoTimeRecursive),
 			SectorsRecursive:        copyBlkioEntry(stats.Blkio.SectorsRecursive),
 		}
 	}
 	if stats.CPU != nil {
9c4570a9
 		s.CPUStats = types.CPUStats{
 			CPUUsage: types.CPUUsage{
ddae20c0
 				TotalUsage:        stats.CPU.Usage.Total,
 				PercpuUsage:       stats.CPU.Usage.PerCPU,
 				UsageInKernelmode: stats.CPU.Usage.Kernel,
 				UsageInUsermode:   stats.CPU.Usage.User,
9c4570a9
 			},
 			ThrottlingData: types.ThrottlingData{
ddae20c0
 				Periods:          stats.CPU.Throttling.Periods,
 				ThrottledPeriods: stats.CPU.Throttling.ThrottledPeriods,
 				ThrottledTime:    stats.CPU.Throttling.ThrottledTime,
9c4570a9
 			},
 		}
ddae20c0
 	}
 
 	if stats.Memory != nil {
 		raw := make(map[string]uint64)
 		raw["cache"] = stats.Memory.Cache
 		raw["rss"] = stats.Memory.RSS
 		raw["rss_huge"] = stats.Memory.RSSHuge
 		raw["mapped_file"] = stats.Memory.MappedFile
 		raw["dirty"] = stats.Memory.Dirty
 		raw["writeback"] = stats.Memory.Writeback
 		raw["pgpgin"] = stats.Memory.PgPgIn
 		raw["pgpgout"] = stats.Memory.PgPgOut
 		raw["pgfault"] = stats.Memory.PgFault
 		raw["pgmajfault"] = stats.Memory.PgMajFault
 		raw["inactive_anon"] = stats.Memory.InactiveAnon
 		raw["active_anon"] = stats.Memory.ActiveAnon
 		raw["inactive_file"] = stats.Memory.InactiveFile
 		raw["active_file"] = stats.Memory.ActiveFile
 		raw["unevictable"] = stats.Memory.Unevictable
 		raw["hierarchical_memory_limit"] = stats.Memory.HierarchicalMemoryLimit
 		raw["hierarchical_memsw_limit"] = stats.Memory.HierarchicalSwapLimit
 		raw["total_cache"] = stats.Memory.TotalCache
 		raw["total_rss"] = stats.Memory.TotalRSS
 		raw["total_rss_huge"] = stats.Memory.TotalRSSHuge
 		raw["total_mapped_file"] = stats.Memory.TotalMappedFile
 		raw["total_dirty"] = stats.Memory.TotalDirty
 		raw["total_writeback"] = stats.Memory.TotalWriteback
 		raw["total_pgpgin"] = stats.Memory.TotalPgPgIn
 		raw["total_pgpgout"] = stats.Memory.TotalPgPgOut
 		raw["total_pgfault"] = stats.Memory.TotalPgFault
 		raw["total_pgmajfault"] = stats.Memory.TotalPgMajFault
 		raw["total_inactive_anon"] = stats.Memory.TotalInactiveAnon
 		raw["total_active_anon"] = stats.Memory.TotalActiveAnon
 		raw["total_inactive_file"] = stats.Memory.TotalInactiveFile
 		raw["total_active_file"] = stats.Memory.TotalActiveFile
 		raw["total_unevictable"] = stats.Memory.TotalUnevictable
 
 		if stats.Memory.Usage != nil {
 			s.MemoryStats = types.MemoryStats{
 				Stats:    raw,
 				Usage:    stats.Memory.Usage.Usage,
 				MaxUsage: stats.Memory.Usage.Max,
 				Limit:    stats.Memory.Usage.Limit,
 				Failcnt:  stats.Memory.Usage.Failcnt,
 			}
 		} else {
 			s.MemoryStats = types.MemoryStats{
 				Stats: raw,
 			}
a0a6d031
 		}
ddae20c0
 
a0a6d031
 		// if the container does not set memory limit, use the machineMemory
ddae20c0
 		if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 {
835971c6
 			s.MemoryStats.Limit = daemon.machineMemory
9c4570a9
 		}
 	}
ddae20c0
 
 	if stats.Pids != nil {
 		s.PidsStats = types.PidsStats{
 			Current: stats.Pids.Current,
 			Limit:   stats.Pids.Limit,
 		}
29b27145
 	}
ddae20c0
 
9c4570a9
 	return s, nil
 }
 
25c9bd81
 // setDefaultIsolation determines the default isolation mode for the
9c4570a9
 // daemon to run in. This is only applicable on Windows
 func (daemon *Daemon) setDefaultIsolation() error {
 	return nil
 }
14dc4a71
 
a894aec8
 // setupDaemonProcess sets various settings for the daemon's process
db63f937
 func setupDaemonProcess(config *config.Config) error {
a894aec8
 	// setup the daemons oom_score_adj
83c2152d
 	if err := setupOOMScoreAdj(config.OOMScoreAdjust); err != nil {
 		return err
 	}
c6a20444
 	if err := setMayDetachMounts(); err != nil {
 		logrus.WithError(err).Warn("Could not set may_detach_mounts kernel parameter")
 	}
 	return nil
83c2152d
 }
 
 // This is used to allow removal of mountpoints that may be mounted in other
 // namespaces on RHEL based kernels starting from RHEL 7.4.
 // Without this setting, removals on these RHEL based kernels may fail with
 // "device or resource busy".
 // This setting is not available in upstream kernels as it is not configurable,
 // but has been in the upstream kernels since 3.15.
 func setMayDetachMounts() error {
 	f, err := os.OpenFile("/proc/sys/fs/may_detach_mounts", os.O_WRONLY, 0)
 	if err != nil {
 		if os.IsNotExist(err) {
 			return nil
 		}
 		return errors.Wrap(err, "error opening may_detach_mounts kernel config file")
 	}
 	defer f.Close()
 
 	_, err = f.WriteString("1")
 	if os.IsPermission(err) {
 		// Setting may_detach_mounts does not work in an
 		// unprivileged container. Ignore the error, but log
 		// it if we appear not to be in that situation.
 		if !rsystem.RunningInUserNS() {
 			logrus.Debugf("Permission denied writing %q to /proc/sys/fs/may_detach_mounts", "1")
 		}
 		return nil
 	}
 	return err
a894aec8
 }
 
 func setupOOMScoreAdj(score int) error {
 	f, err := os.OpenFile("/proc/self/oom_score_adj", os.O_WRONLY, 0)
 	if err != nil {
 		return err
 	}
e82fa89b
 	defer f.Close()
9ed54d3c
 	stringScore := strconv.Itoa(score)
 	_, err = f.WriteString(stringScore)
32f24bc3
 	if os.IsPermission(err) {
 		// Setting oom_score_adj does not work in an
9ed54d3c
 		// unprivileged container. Ignore the error, but log
 		// it if we appear not to be in that situation.
 		if !rsystem.RunningInUserNS() {
 			logrus.Debugf("Permission denied writing %q to /proc/self/oom_score_adj", stringScore)
 		}
32f24bc3
 		return nil
 	}
e82fa89b
 
a894aec8
 	return err
 }
56f77d5a
 
 func (daemon *Daemon) initCgroupsPath(path string) error {
 	if path == "/" || path == "." {
 		return nil
 	}
 
f285d5b3
 	if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 {
 		return nil
 	}
 
 	// Recursively create cgroup to ensure that the system and all parent cgroups have values set
 	// for the period and runtime as this limits what the children can be set to.
56f77d5a
 	daemon.initCgroupsPath(filepath.Dir(path))
 
78045a54
 	mnt, root, err := cgroups.FindCgroupMountpointAndRoot("", "cpu")
56f77d5a
 	if err != nil {
 		return err
 	}
40e07553
 	// When docker is run inside docker, the root is based of the host cgroup.
 	// Should this be handled in runc/libcontainer/cgroups ?
 	if strings.HasPrefix(root, "/docker/") {
 		root = "/"
 	}
56f77d5a
 
40e07553
 	path = filepath.Join(mnt, root, path)
ff42a2eb
 	sysinfo := sysinfo.New(true)
f7819fcb
 	if err := maybeCreateCPURealTimeFile(sysinfo.CPURealtimePeriod, daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
 		return err
56f77d5a
 	}
b4a63139
 	return maybeCreateCPURealTimeFile(sysinfo.CPURealtimeRuntime, daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path)
f7819fcb
 }
 
 func maybeCreateCPURealTimeFile(sysinfoPresent bool, configValue int64, file string, path string) error {
 	if sysinfoPresent && configValue != 0 {
516010e9
 		if err := os.MkdirAll(path, 0755); err != nil {
f285d5b3
 			return err
 		}
f7819fcb
 		if err := ioutil.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700); err != nil {
56f77d5a
 			return err
 		}
 	}
b237189e
 	return nil
 }
56f77d5a
 
b237189e
 func (daemon *Daemon) setupSeccompProfile() error {
 	if daemon.configStore.SeccompProfile != "" {
 		daemon.seccompProfilePath = daemon.configStore.SeccompProfile
 		b, err := ioutil.ReadFile(daemon.configStore.SeccompProfile)
 		if err != nil {
 			return fmt.Errorf("opening seccomp profile (%s) failed: %v", daemon.configStore.SeccompProfile, err)
 		}
 		daemon.seccompProfile = b
 	}
56f77d5a
 	return nil
 }