daemon/daemon_unix.go
10d30c64
 // +build linux freebsd
8fb0ca2c
 
 package daemon
 
 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"
4352da78
 	"github.com/docker/docker/image"
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"
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"
4a8799dc
 	"github.com/docker/docker/volume"
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"
56f77d5a
 	specs "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
 	DefaultShimBinary = "docker-containerd-shim"
 
 	// DefaultRuntimeBinary is the default runtime to be used by
 	// containerd if none is specified
 	DefaultRuntimeBinary = "docker-runc"
 
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
 	defaultIDSpecifier string = "default"
 	defaultRemappedID  string = "dockremap"
8af4f89c
 
 	// constant for cgroup drivers
 	cgroupFsDriver      = "cgroupfs"
 	cgroupSystemdDriver = "systemd"
ddae20c0
 
 	// DefaultRuntimeName is the default runtime to be used by
 	// containerd if none is specified
 	DefaultRuntimeName = "docker-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
 	}
 
 	if config.KernelMemory != 0 {
45d85c99
 		memory.Kernel = &config.KernelMemory
9c4570a9
 	}
 
 	return &memory
 }
 
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}
9c4570a9
 		d.Major = int64(stat.Rdev / 256)
8784bd6f
 		d.Minor = int64(stat.Rdev % 256)
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}
9c4570a9
 		d.Major = int64(stat.Rdev / 256)
8784bd6f
 		d.Minor = int64(stat.Rdev % 256)
668f0a2a
 		throttleDevices = append(throttleDevices, d)
843084b0
 	}
 
668f0a2a
 	return throttleDevices, nil
3f15a055
 }
 
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)
 		}
 	}
 }
 
8ae6f6ac
 func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo, update bool) ([]string, error) {
b2d06b6f
 	warnings := []string{}
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.")
 		logrus.Warn("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.")
 		logrus.Warn("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.")
 		logrus.Warn("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.")
 		logrus.Warn("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.")
 		logrus.Warn("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.")
44ccbb31
 		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.")
b6f1b4ad
 	}
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.")
 			logrus.Warn("Your kernel does not support OomKillDisable. OomKillDisable discarded.")
5a707d94
 		}
f4a68733
 		resources.OomKillDisable = nil
8498ed73
 	}
c6bfb54a
 
69cf0370
 	if resources.PidsLimit != 0 && !sysInfo.PidsLimit {
846f33f9
 		warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
 		logrus.Warn("Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
69cf0370
 		resources.PidsLimit = 0
 	}
 
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.")
 		logrus.Warn("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.")
 		logrus.Warn("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.")
 		logrus.Warn("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))
d9177233
 		logrus.Warnf("%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.")
 		logrus.Warn("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 {
aae4bcf7
 		return warnings, fmt.Errorf("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 {
aae4bcf7
 		return warnings, fmt.Errorf("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.")
 		logrus.Warn("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.")
 		logrus.Warn("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.")
 		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")
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.")
 		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.")
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.")
 		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.")
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.")
 		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.")
843084b0
 		resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{}
 	}
c6bfb54a
 
 	return warnings, nil
 }
 
ca89c329
 func (daemon *Daemon) getCgroupDriver() string {
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
 	}
 	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.
8ae6f6ac
 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
6306019d
 	var warnings []string
c6bfb54a
 	sysInfo := sysinfo.New(true)
 
8ae6f6ac
 	w, err := verifyContainerResources(&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.")
44ccbb31
 		logrus.Warn("IPv4 forwarding is disabled. Networking will not work")
8fb0ca2c
 	}
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)
 	}
 
e89b6e8c
 	parser := volume.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 {
 		return errors.Wrapf(err, "failed to get temp dir to generate runtime scripts")
 	}
 	defer func() {
 		if err != nil {
 			if err1 := os.RemoveAll(tmpDir); err1 != nil {
 				logrus.WithError(err1).WithField("dir", tmpDir).
 					Warnf("failed to remove tmp dir")
 			}
 			return
 		}
 
 		if err = os.Rename(runtimeDir, runtimeDir+"-old"); err != nil {
 			return
 		}
 		if err = os.Rename(tmpDir, runtimeDir); err != nil {
 			err = errors.Wrapf(err, "failed to setup runtimes dir, new containers may not start")
 			return
 		}
 		if err = os.RemoveAll(runtimeDir + "-old"); err != nil {
 			logrus.WithError(err).WithField("dir", tmpDir).
 				Warnf("failed to remove old runtimes dir")
 		}
 	}()
 
 	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 {
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 {
 	if os.Geteuid() != 0 {
 		return fmt.Errorf("The Docker daemon needs to be run as root")
 	}
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)
 		}
 	}
 
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)
 		}
 	}
 }
 
7a7357da
 func (daemon *Daemon) getLayerInit() func(containerfs.ContainerFS) error {
2508ca00
 	return daemon.setupInitLayer
 }
 
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
 }
 
09cd96c5
 func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, 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")
09cd96c5
 			return &idtools.IDMappings{}, 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)
 
09cd96c5
 		mappings, err := idtools.NewIDMappings(username, groupname)
557c7cb8
 		if err != nil {
09cd96c5
 			return nil, errors.Wrapf(err, "Can't create ID mappings: %v")
557c7cb8
 		}
09cd96c5
 		return mappings, nil
557c7cb8
 	}
09cd96c5
 	return &idtools.IDMappings{}, nil
557c7cb8
 }
 
09cd96c5
 func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) 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 != "" {
09cd96c5
 		config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIDs.UID, rootIDs.GID))
557c7cb8
 		logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
25c9bd81
 		// Create the root directory if it doesn't exist
09cd96c5
 		if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIDs); 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
 			}
09cd96c5
 			if !idtools.CanAccess(dirPath, rootIDs) {
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
 	}
 	return nil
 }
 
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 {
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 {
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
 
 func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
 	var layers []string
 	for _, l := range rootfs.DiffIDs {
 		layers = append(layers, l.String())
 	}
 	return types.RootFS{
 		Type:   rootfs.Type,
 		Layers: layers,
 	}
 }
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))
 
40e07553
 	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
 }