summaryrefslogtreecommitdiff
path: root/pkg/sysinfo
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sysinfo')
-rw-r--r--pkg/sysinfo/README.md1
-rw-r--r--pkg/sysinfo/numcpu.go12
-rw-r--r--pkg/sysinfo/numcpu_linux.go44
-rw-r--r--pkg/sysinfo/numcpu_windows.go37
-rw-r--r--pkg/sysinfo/sysinfo.go144
-rw-r--r--pkg/sysinfo/sysinfo_linux.go254
-rw-r--r--pkg/sysinfo/sysinfo_linux.go~254
-rw-r--r--pkg/sysinfo/sysinfo_linux_test.go104
-rw-r--r--pkg/sysinfo/sysinfo_solaris.go121
-rw-r--r--pkg/sysinfo/sysinfo_test.go26
-rw-r--r--pkg/sysinfo/sysinfo_unix.go9
-rw-r--r--pkg/sysinfo/sysinfo_windows.go9
12 files changed, 1015 insertions, 0 deletions
diff --git a/pkg/sysinfo/README.md b/pkg/sysinfo/README.md
new file mode 100644
index 000000000..c1530cef0
--- /dev/null
+++ b/pkg/sysinfo/README.md
@@ -0,0 +1 @@
+SysInfo stores information about which features a kernel supports.
diff --git a/pkg/sysinfo/numcpu.go b/pkg/sysinfo/numcpu.go
new file mode 100644
index 000000000..aeb1a3a80
--- /dev/null
+++ b/pkg/sysinfo/numcpu.go
@@ -0,0 +1,12 @@
+// +build !linux,!windows
+
+package sysinfo
+
+import (
+ "runtime"
+)
+
+// NumCPU returns the number of CPUs
+func NumCPU() int {
+ return runtime.NumCPU()
+}
diff --git a/pkg/sysinfo/numcpu_linux.go b/pkg/sysinfo/numcpu_linux.go
new file mode 100644
index 000000000..f1d2d9db3
--- /dev/null
+++ b/pkg/sysinfo/numcpu_linux.go
@@ -0,0 +1,44 @@
+// +build linux
+
+package sysinfo
+
+import (
+ "runtime"
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+// numCPU queries the system for the count of threads available
+// for use to this process.
+//
+// Issues two syscalls.
+// Returns 0 on errors. Use |runtime.NumCPU| in that case.
+func numCPU() int {
+ // Gets the affinity mask for a process: The very one invoking this function.
+ pid, _, _ := unix.RawSyscall(unix.SYS_GETPID, 0, 0, 0)
+
+ var mask [1024 / 64]uintptr
+ _, _, err := unix.RawSyscall(unix.SYS_SCHED_GETAFFINITY, pid, uintptr(len(mask)*8), uintptr(unsafe.Pointer(&mask[0])))
+ if err != 0 {
+ return 0
+ }
+
+ // For every available thread a bit is set in the mask.
+ ncpu := 0
+ for _, e := range mask {
+ if e == 0 {
+ continue
+ }
+ ncpu += int(popcnt(uint64(e)))
+ }
+ return ncpu
+}
+
+// NumCPU returns the number of CPUs which are currently online
+func NumCPU() int {
+ if ncpu := numCPU(); ncpu > 0 {
+ return ncpu
+ }
+ return runtime.NumCPU()
+}
diff --git a/pkg/sysinfo/numcpu_windows.go b/pkg/sysinfo/numcpu_windows.go
new file mode 100644
index 000000000..1d89dd550
--- /dev/null
+++ b/pkg/sysinfo/numcpu_windows.go
@@ -0,0 +1,37 @@
+// +build windows
+
+package sysinfo
+
+import (
+ "runtime"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var (
+ kernel32 = windows.NewLazySystemDLL("kernel32.dll")
+ getCurrentProcess = kernel32.NewProc("GetCurrentProcess")
+ getProcessAffinityMask = kernel32.NewProc("GetProcessAffinityMask")
+)
+
+func numCPU() int {
+ // Gets the affinity mask for a process
+ var mask, sysmask uintptr
+ currentProcess, _, _ := getCurrentProcess.Call()
+ ret, _, _ := getProcessAffinityMask.Call(currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
+ if ret == 0 {
+ return 0
+ }
+ // For every available thread a bit is set in the mask.
+ ncpu := int(popcnt(uint64(mask)))
+ return ncpu
+}
+
+// NumCPU returns the number of CPUs which are currently online
+func NumCPU() int {
+ if ncpu := numCPU(); ncpu > 0 {
+ return ncpu
+ }
+ return runtime.NumCPU()
+}
diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go
new file mode 100644
index 000000000..f046de4b1
--- /dev/null
+++ b/pkg/sysinfo/sysinfo.go
@@ -0,0 +1,144 @@
+package sysinfo
+
+import "github.com/docker/docker/pkg/parsers"
+
+// SysInfo stores information about which features a kernel supports.
+// TODO Windows: Factor out platform specific capabilities.
+type SysInfo struct {
+ // Whether the kernel supports AppArmor or not
+ AppArmor bool
+ // Whether the kernel supports Seccomp or not
+ Seccomp bool
+
+ cgroupMemInfo
+ cgroupCPUInfo
+ cgroupBlkioInfo
+ cgroupCpusetInfo
+ cgroupPids
+
+ // Whether IPv4 forwarding is supported or not, if this was disabled, networking will not work
+ IPv4ForwardingDisabled bool
+
+ // Whether bridge-nf-call-iptables is supported or not
+ BridgeNFCallIPTablesDisabled bool
+
+ // Whether bridge-nf-call-ip6tables is supported or not
+ BridgeNFCallIP6TablesDisabled bool
+
+ // Whether the cgroup has the mountpoint of "devices" or not
+ CgroupDevicesEnabled bool
+}
+
+type cgroupMemInfo struct {
+ // Whether memory limit is supported or not
+ MemoryLimit bool
+
+ // Whether swap limit is supported or not
+ SwapLimit bool
+
+ // Whether soft limit is supported or not
+ MemoryReservation bool
+
+ // Whether OOM killer disable is supported or not
+ OomKillDisable bool
+
+ // Whether memory swappiness is supported or not
+ MemorySwappiness bool
+
+ // Whether kernel memory limit is supported or not
+ KernelMemory bool
+}
+
+type cgroupCPUInfo struct {
+ // Whether CPU shares is supported or not
+ CPUShares bool
+
+ // Whether CPU CFS(Completely Fair Scheduler) period is supported or not
+ CPUCfsPeriod bool
+
+ // Whether CPU CFS(Completely Fair Scheduler) quota is supported or not
+ CPUCfsQuota bool
+
+ // Whether CPU real-time period is supported or not
+ CPURealtimePeriod bool
+
+ // Whether CPU real-time runtime is supported or not
+ CPURealtimeRuntime bool
+}
+
+type cgroupBlkioInfo struct {
+ // Whether Block IO weight is supported or not
+ BlkioWeight bool
+
+ // Whether Block IO weight_device is supported or not
+ BlkioWeightDevice bool
+
+ // Whether Block IO read limit in bytes per second is supported or not
+ BlkioReadBpsDevice bool
+
+ // Whether Block IO write limit in bytes per second is supported or not
+ BlkioWriteBpsDevice bool
+
+ // Whether Block IO read limit in IO per second is supported or not
+ BlkioReadIOpsDevice bool
+
+ // Whether Block IO write limit in IO per second is supported or not
+ BlkioWriteIOpsDevice bool
+}
+
+type cgroupCpusetInfo struct {
+ // Whether Cpuset is supported or not
+ Cpuset bool
+
+ // Available Cpuset's cpus
+ Cpus string
+
+ // Available Cpuset's memory nodes
+ Mems string
+}
+
+type cgroupPids struct {
+ // Whether Pids Limit is supported or not
+ PidsLimit bool
+}
+
+// IsCpusetCpusAvailable returns `true` if the provided string set is contained
+// in cgroup's cpuset.cpus set, `false` otherwise.
+// If error is not nil a parsing error occurred.
+func (c cgroupCpusetInfo) IsCpusetCpusAvailable(provided string) (bool, error) {
+ return isCpusetListAvailable(provided, c.Cpus)
+}
+
+// IsCpusetMemsAvailable returns `true` if the provided string set is contained
+// in cgroup's cpuset.mems set, `false` otherwise.
+// If error is not nil a parsing error occurred.
+func (c cgroupCpusetInfo) IsCpusetMemsAvailable(provided string) (bool, error) {
+ return isCpusetListAvailable(provided, c.Mems)
+}
+
+func isCpusetListAvailable(provided, available string) (bool, error) {
+ parsedProvided, err := parsers.ParseUintList(provided)
+ if err != nil {
+ return false, err
+ }
+ parsedAvailable, err := parsers.ParseUintList(available)
+ if err != nil {
+ return false, err
+ }
+ for k := range parsedProvided {
+ if !parsedAvailable[k] {
+ return false, nil
+ }
+ }
+ return true, nil
+}
+
+// Returns bit count of 1, used by NumCPU
+func popcnt(x uint64) (n byte) {
+ x -= (x >> 1) & 0x5555555555555555
+ x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
+ x += x >> 4
+ x &= 0x0f0f0f0f0f0f0f0f
+ x *= 0x0101010101010101
+ return byte(x >> 56)
+}
diff --git a/pkg/sysinfo/sysinfo_linux.go b/pkg/sysinfo/sysinfo_linux.go
new file mode 100644
index 000000000..f4047b63c
--- /dev/null
+++ b/pkg/sysinfo/sysinfo_linux.go
@@ -0,0 +1,254 @@
+package sysinfo
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+func findCgroupMountpoints() (map[string]string, error) {
+ cgMounts, err := cgroups.GetCgroupMounts(false)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
+ }
+ mps := make(map[string]string)
+ for _, m := range cgMounts {
+ for _, ss := range m.Subsystems {
+ mps[ss] = m.Mountpoint
+ }
+ }
+ return mps, nil
+}
+
+// New returns a new SysInfo, using the filesystem to detect which features
+// the kernel supports. If `quiet` is `false` warnings are printed in logs
+// whenever an error occurs or misconfigurations are present.
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ cgMounts, err := findCgroupMountpoints()
+ if err != nil {
+ logrus.Warnf("Failed to parse cgroup information: %v", err)
+ } else {
+ sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
+ sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
+ sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
+ sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
+ sysInfo.cgroupPids = checkCgroupPids(quiet)
+ }
+
+ _, ok := cgMounts["devices"]
+ sysInfo.CgroupDevicesEnabled = ok
+
+ sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
+ sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
+ sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
+
+ // Check if AppArmor is supported.
+ if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
+ sysInfo.AppArmor = true
+ }
+
+ // Check if Seccomp is supported, via CONFIG_SECCOMP.
+ if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
+ // Make sure the kernel has CONFIG_SECCOMP_FILTER.
+ if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
+ sysInfo.Seccomp = true
+ }
+ }
+
+ return sysInfo
+}
+
+// checkCgroupMem reads the memory information from the memory cgroup mount point.
+func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
+ mountPoint, ok := cgMounts["memory"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Your kernel does not support cgroup memory limit")
+ }
+ return cgroupMemInfo{}
+ }
+
+ swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
+ if !quiet && !swapLimit {
+ logrus.Warn("Your kernel does not support swap memory limit")
+ }
+ memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
+ if !quiet && !memoryReservation {
+ logrus.Warn("Your kernel does not support memory reservation")
+ }
+ oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
+ if !quiet && !oomKillDisable {
+ logrus.Warn("Your kernel does not support oom control")
+ }
+ memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
+ if !quiet && !memorySwappiness {
+ logrus.Warn("Your kernel does not support memory swappiness")
+ }
+ kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
+ if !quiet && !kernelMemory {
+ logrus.Warn("Your kernel does not support kernel memory limit")
+ }
+
+ return cgroupMemInfo{
+ MemoryLimit: true,
+ SwapLimit: swapLimit,
+ MemoryReservation: memoryReservation,
+ OomKillDisable: oomKillDisable,
+ MemorySwappiness: memorySwappiness,
+ KernelMemory: kernelMemory,
+ }
+}
+
+// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
+func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
+ mountPoint, ok := cgMounts["cpu"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find cpu cgroup in mounts")
+ }
+ return cgroupCPUInfo{}
+ }
+
+ cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
+ if !quiet && !cpuShares {
+ logrus.Warn("Your kernel does not support cgroup cpu shares")
+ }
+
+ cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
+ if !quiet && !cpuCfsPeriod {
+ logrus.Warn("Your kernel does not support cgroup cfs period")
+ }
+
+ cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
+ if !quiet && !cpuCfsQuota {
+ logrus.Warn("Your kernel does not support cgroup cfs quotas")
+ }
+
+ cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
+ if !quiet && !cpuRealtimePeriod {
+ logrus.Warn("Your kernel does not support cgroup rt period")
+ }
+
+ cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
+ if !quiet && !cpuRealtimeRuntime {
+ logrus.Warn("Your kernel does not support cgroup rt runtime")
+ }
+
+ return cgroupCPUInfo{
+ CPUShares: cpuShares,
+ CPUCfsPeriod: cpuCfsPeriod,
+ CPUCfsQuota: cpuCfsQuota,
+ CPURealtimePeriod: cpuRealtimePeriod,
+ CPURealtimeRuntime: cpuRealtimeRuntime,
+ }
+}
+
+// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
+func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
+ mountPoint, ok := cgMounts["blkio"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find blkio cgroup in mounts")
+ }
+ return cgroupBlkioInfo{}
+ }
+
+ weight := cgroupEnabled(mountPoint, "blkio.weight")
+ if !quiet && !weight {
+ logrus.Warn("Your kernel does not support cgroup blkio weight")
+ }
+
+ weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
+ if !quiet && !weightDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio weight_device")
+ }
+
+ readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
+ if !quiet && !readBpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
+ }
+
+ writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
+ if !quiet && !writeBpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
+ }
+ readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
+ if !quiet && !readIOpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
+ }
+
+ writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
+ if !quiet && !writeIOpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
+ }
+ return cgroupBlkioInfo{
+ BlkioWeight: weight,
+ BlkioWeightDevice: weightDevice,
+ BlkioReadBpsDevice: readBpsDevice,
+ BlkioWriteBpsDevice: writeBpsDevice,
+ BlkioReadIOpsDevice: readIOpsDevice,
+ BlkioWriteIOpsDevice: writeIOpsDevice,
+ }
+}
+
+// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
+func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
+ mountPoint, ok := cgMounts["cpuset"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find cpuset cgroup in mounts")
+ }
+ return cgroupCpusetInfo{}
+ }
+
+ cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
+ if err != nil {
+ return cgroupCpusetInfo{}
+ }
+
+ mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
+ if err != nil {
+ return cgroupCpusetInfo{}
+ }
+
+ return cgroupCpusetInfo{
+ Cpuset: true,
+ Cpus: strings.TrimSpace(string(cpus)),
+ Mems: strings.TrimSpace(string(mems)),
+ }
+}
+
+// checkCgroupPids reads the pids information from the pids cgroup mount point.
+func checkCgroupPids(quiet bool) cgroupPids {
+ _, err := cgroups.FindCgroupMountpoint("", "pids")
+ if err != nil {
+ if !quiet {
+ logrus.Warn(err)
+ }
+ return cgroupPids{}
+ }
+
+ return cgroupPids{
+ PidsLimit: true,
+ }
+}
+
+func cgroupEnabled(mountPoint, name string) bool {
+ _, err := os.Stat(path.Join(mountPoint, name))
+ return err == nil
+}
+
+func readProcBool(path string) bool {
+ val, err := ioutil.ReadFile(path)
+ if err != nil {
+ return false
+ }
+ return strings.TrimSpace(string(val)) == "1"
+}
diff --git a/pkg/sysinfo/sysinfo_linux.go~ b/pkg/sysinfo/sysinfo_linux.go~
new file mode 100644
index 000000000..471f786a7
--- /dev/null
+++ b/pkg/sysinfo/sysinfo_linux.go~
@@ -0,0 +1,254 @@
+package sysinfo
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+func findCgroupMountpoints() (map[string]string, error) {
+ cgMounts, err := cgroups.GetCgroupMounts(false)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
+ }
+ mps := make(map[string]string)
+ for _, m := range cgMounts {
+ for _, ss := range m.Subsystems {
+ mps[ss] = m.Mountpoint
+ }
+ }
+ return mps, nil
+}
+
+// New returns a new SysInfo, using the filesystem to detect which features
+// the kernel supports. If `quiet` is `false` warnings are printed in logs
+// whenever an error occurs or misconfigurations are present.
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ cgMounts, err := findCgroupMountpoints()
+ if err != nil {
+ logrus.Warnf("Failed to parse cgroup information: %v", err)
+ } else {
+ sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
+ sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
+ sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
+ sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
+ sysInfo.cgroupPids = checkCgroupPids(quiet)
+ }
+
+ _, ok := cgMounts["devices"]
+ sysInfo.CgroupDevicesEnabled = ok
+
+ sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
+ sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
+ sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
+
+ // Check if AppArmor is supported.
+ if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
+ sysInfo.AppArmor = true
+ }
+
+ // Check if Seccomp is supported, via CONFIG_SECCOMP.
+ if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
+ // Make sure the kernel has CONFIG_SECCOMP_FILTER.
+ if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
+ sysInfo.Seccomp = true
+ }
+ }
+
+ return sysInfo
+}
+
+// checkCgroupMem reads the memory information from the memory cgroup mount point.
+func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
+ mountPoint, ok := cgMounts["memory"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Your kernel does not support cgroup memory limit")
+ }
+ return cgroupMemInfo{}
+ }
+
+ swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
+ if !quiet && !swapLimit {
+ logrus.Warn("Your kernel does not support swap memory limit")
+ }
+ memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
+ if !quiet && !memoryReservation {
+ logrus.Warn("Your kernel does not support memory reservation")
+ }
+ oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
+ if !quiet && !oomKillDisable {
+ logrus.Warn("Your kernel does not support oom control")
+ }
+ memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
+ if !quiet && !memorySwappiness {
+ logrus.Warn("Your kernel does not support memory swappiness")
+ }
+ kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
+ if !quiet && !kernelMemory {
+ logrus.Warn("Your kernel does not support kernel memory limit")
+ }
+
+ return cgroupMemInfo{
+ MemoryLimit: true,
+ SwapLimit: swapLimit,
+ MemoryReservation: memoryReservation,
+ OomKillDisable: oomKillDisable,
+ MemorySwappiness: memorySwappiness,
+ KernelMemory: kernelMemory,
+ }
+}
+
+// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
+func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
+ mountPoint, ok := cgMounts["cpu"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find cpu cgroup in mounts")
+ }
+ return cgroupCPUInfo{}
+ }
+
+ cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
+ if !quiet && !cpuShares {
+ logrus.Warn("Your kernel does not support cgroup cpu shares")
+ }
+
+ cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
+ if !quiet && !cpuCfsPeriod {
+ logrus.Warn("Your kernel does not support cgroup cfs period")
+ }
+
+ cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
+ if !quiet && !cpuCfsQuota {
+ logrus.Warn("Your kernel does not support cgroup cfs quotas")
+ }
+
+ cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
+ if !quiet && !cpuRealtimePeriod {
+ logrus.Warn("Your kernel does not support cgroup rt period")
+ }
+
+ cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
+ if !quiet && !cpuRealtimeRuntime {
+ logrus.Warn("Your kernel does not support cgroup rt runtime")
+ }
+
+ return cgroupCPUInfo{
+ CPUShares: cpuShares,
+ CPUCfsPeriod: cpuCfsPeriod,
+ CPUCfsQuota: cpuCfsQuota,
+ CPURealtimePeriod: cpuRealtimePeriod,
+ CPURealtimeRuntime: cpuRealtimeRuntime,
+ }
+}
+
+// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
+func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
+ mountPoint, ok := cgMounts["blkio"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find blkio cgroup in mounts")
+ }
+ return cgroupBlkioInfo{}
+ }
+
+ weight := cgroupEnabled(mountPoint, "blkio.weight")
+ if !quiet && !weight {
+ logrus.Warn("Your kernel does not support cgroup blkio weight")
+ }
+
+ weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
+ if !quiet && !weightDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio weight_device")
+ }
+
+ readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
+ if !quiet && !readBpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
+ }
+
+ writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
+ if !quiet && !writeBpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
+ }
+ readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
+ if !quiet && !readIOpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
+ }
+
+ writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
+ if !quiet && !writeIOpsDevice {
+ logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
+ }
+ return cgroupBlkioInfo{
+ BlkioWeight: weight,
+ BlkioWeightDevice: weightDevice,
+ BlkioReadBpsDevice: readBpsDevice,
+ BlkioWriteBpsDevice: writeBpsDevice,
+ BlkioReadIOpsDevice: readIOpsDevice,
+ BlkioWriteIOpsDevice: writeIOpsDevice,
+ }
+}
+
+// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
+func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
+ mountPoint, ok := cgMounts["cpuset"]
+ if !ok {
+ if !quiet {
+ logrus.Warn("Unable to find cpuset cgroup in mounts")
+ }
+ return cgroupCpusetInfo{}
+ }
+
+ cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
+ if err != nil {
+ return cgroupCpusetInfo{}
+ }
+
+ mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
+ if err != nil {
+ return cgroupCpusetInfo{}
+ }
+
+ return cgroupCpusetInfo{
+ Cpuset: true,
+ Cpus: strings.TrimSpace(string(cpus)),
+ Mems: strings.TrimSpace(string(mems)),
+ }
+}
+
+// checkCgroupPids reads the pids information from the pids cgroup mount point.
+func checkCgroupPids(quiet bool) cgroupPids {
+ _, err := cgroups.FindCgroupMountpoint("pids")
+ if err != nil {
+ if !quiet {
+ logrus.Warn(err)
+ }
+ return cgroupPids{}
+ }
+
+ return cgroupPids{
+ PidsLimit: true,
+ }
+}
+
+func cgroupEnabled(mountPoint, name string) bool {
+ _, err := os.Stat(path.Join(mountPoint, name))
+ return err == nil
+}
+
+func readProcBool(path string) bool {
+ val, err := ioutil.ReadFile(path)
+ if err != nil {
+ return false
+ }
+ return strings.TrimSpace(string(val)) == "1"
+}
diff --git a/pkg/sysinfo/sysinfo_linux_test.go b/pkg/sysinfo/sysinfo_linux_test.go
new file mode 100644
index 000000000..860784f2a
--- /dev/null
+++ b/pkg/sysinfo/sysinfo_linux_test.go
@@ -0,0 +1,104 @@
+package sysinfo
+
+import (
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "golang.org/x/sys/unix"
+)
+
+func TestReadProcBool(t *testing.T) {
+ tmpDir, err := ioutil.TempDir("", "test-sysinfo-proc")
+ require.NoError(t, err)
+ defer os.RemoveAll(tmpDir)
+
+ procFile := filepath.Join(tmpDir, "read-proc-bool")
+ err = ioutil.WriteFile(procFile, []byte("1"), 0644)
+ require.NoError(t, err)
+
+ if !readProcBool(procFile) {
+ t.Fatal("expected proc bool to be true, got false")
+ }
+
+ if err := ioutil.WriteFile(procFile, []byte("0"), 0644); err != nil {
+ t.Fatal(err)
+ }
+ if readProcBool(procFile) {
+ t.Fatal("expected proc bool to be false, got true")
+ }
+
+ if readProcBool(path.Join(tmpDir, "no-exist")) {
+ t.Fatal("should be false for non-existent entry")
+ }
+
+}
+
+func TestCgroupEnabled(t *testing.T) {
+ cgroupDir, err := ioutil.TempDir("", "cgroup-test")
+ require.NoError(t, err)
+ defer os.RemoveAll(cgroupDir)
+
+ if cgroupEnabled(cgroupDir, "test") {
+ t.Fatal("cgroupEnabled should be false")
+ }
+
+ err = ioutil.WriteFile(path.Join(cgroupDir, "test"), []byte{}, 0644)
+ require.NoError(t, err)
+
+ if !cgroupEnabled(cgroupDir, "test") {
+ t.Fatal("cgroupEnabled should be true")
+ }
+}
+
+func TestNew(t *testing.T) {
+ sysInfo := New(false)
+ require.NotNil(t, sysInfo)
+ checkSysInfo(t, sysInfo)
+
+ sysInfo = New(true)
+ require.NotNil(t, sysInfo)
+ checkSysInfo(t, sysInfo)
+}
+
+func checkSysInfo(t *testing.T, sysInfo *SysInfo) {
+ // Check if Seccomp is supported, via CONFIG_SECCOMP.then sysInfo.Seccomp must be TRUE , else FALSE
+ if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
+ // Make sure the kernel has CONFIG_SECCOMP_FILTER.
+ if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
+ require.True(t, sysInfo.Seccomp)
+ }
+ } else {
+ require.False(t, sysInfo.Seccomp)
+ }
+}
+
+func TestNewAppArmorEnabled(t *testing.T) {
+ // Check if AppArmor is supported. then it must be TRUE , else FALSE
+ if _, err := os.Stat("/sys/kernel/security/apparmor"); err != nil {
+ t.Skip("App Armor Must be Enabled")
+ }
+
+ sysInfo := New(true)
+ require.True(t, sysInfo.AppArmor)
+}
+
+func TestNewAppArmorDisabled(t *testing.T) {
+ // Check if AppArmor is supported. then it must be TRUE , else FALSE
+ if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
+ t.Skip("App Armor Must be Disabled")
+ }
+
+ sysInfo := New(true)
+ require.False(t, sysInfo.AppArmor)
+}
+
+func TestNumCPU(t *testing.T) {
+ cpuNumbers := NumCPU()
+ if cpuNumbers <= 0 {
+ t.Fatal("CPU returned must be greater than zero")
+ }
+}
diff --git a/pkg/sysinfo/sysinfo_solaris.go b/pkg/sysinfo/sysinfo_solaris.go
new file mode 100644
index 000000000..c858d57e0
--- /dev/null
+++ b/pkg/sysinfo/sysinfo_solaris.go
@@ -0,0 +1,121 @@
+// +build solaris,cgo
+
+package sysinfo
+
+import (
+ "bytes"
+ "os/exec"
+ "strconv"
+ "strings"
+)
+
+/*
+#cgo LDFLAGS: -llgrp
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/lgrp_user.h>
+int getLgrpCount() {
+ lgrp_cookie_t lgrpcookie = LGRP_COOKIE_NONE;
+ uint_t nlgrps;
+
+ if ((lgrpcookie = lgrp_init(LGRP_VIEW_OS)) == LGRP_COOKIE_NONE) {
+ return -1;
+ }
+ nlgrps = lgrp_nlgrps(lgrpcookie);
+ return nlgrps;
+}
+*/
+import "C"
+
+// IsCPUSharesAvailable returns whether CPUShares setting is supported.
+// We need FSS to be set as default scheduling class to support CPU Shares
+func IsCPUSharesAvailable() bool {
+ cmd := exec.Command("/usr/sbin/dispadmin", "-d")
+ outBuf := new(bytes.Buffer)
+ errBuf := new(bytes.Buffer)
+ cmd.Stderr = errBuf
+ cmd.Stdout = outBuf
+
+ if err := cmd.Run(); err != nil {
+ return false
+ }
+ return (strings.Contains(outBuf.String(), "FSS"))
+}
+
+// New returns a new SysInfo, using the filesystem to detect which features
+// the kernel supports.
+//NOTE Solaris: If we change the below capabilities be sure
+// to update verifyPlatformContainerSettings() in daemon_solaris.go
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ sysInfo.cgroupMemInfo = setCgroupMem(quiet)
+ sysInfo.cgroupCPUInfo = setCgroupCPU(quiet)
+ sysInfo.cgroupBlkioInfo = setCgroupBlkioInfo(quiet)
+ sysInfo.cgroupCpusetInfo = setCgroupCPUsetInfo(quiet)
+
+ sysInfo.IPv4ForwardingDisabled = false
+
+ sysInfo.AppArmor = false
+
+ return sysInfo
+}
+
+// setCgroupMem reads the memory information for Solaris.
+func setCgroupMem(quiet bool) cgroupMemInfo {
+
+ return cgroupMemInfo{
+ MemoryLimit: true,
+ SwapLimit: true,
+ MemoryReservation: false,
+ OomKillDisable: false,
+ MemorySwappiness: false,
+ KernelMemory: false,
+ }
+}
+
+// setCgroupCPU reads the cpu information for Solaris.
+func setCgroupCPU(quiet bool) cgroupCPUInfo {
+
+ return cgroupCPUInfo{
+ CPUShares: true,
+ CPUCfsPeriod: false,
+ CPUCfsQuota: true,
+ CPURealtimePeriod: false,
+ CPURealtimeRuntime: false,
+ }
+}
+
+// blkio switches are not supported in Solaris.
+func setCgroupBlkioInfo(quiet bool) cgroupBlkioInfo {
+
+ return cgroupBlkioInfo{
+ BlkioWeight: false,
+ BlkioWeightDevice: false,
+ }
+}
+
+// setCgroupCPUsetInfo reads the cpuset information for Solaris.
+func setCgroupCPUsetInfo(quiet bool) cgroupCpusetInfo {
+
+ return cgroupCpusetInfo{
+ Cpuset: true,
+ Cpus: getCPUCount(),
+ Mems: getLgrpCount(),
+ }
+}
+
+func getCPUCount() string {
+ ncpus := C.sysconf(C._SC_NPROCESSORS_ONLN)
+ if ncpus <= 0 {
+ return ""
+ }
+ return strconv.FormatInt(int64(ncpus), 16)
+}
+
+func getLgrpCount() string {
+ nlgrps := C.getLgrpCount()
+ if nlgrps <= 0 {
+ return ""
+ }
+ return strconv.FormatInt(int64(nlgrps), 16)
+}
diff --git a/pkg/sysinfo/sysinfo_test.go b/pkg/sysinfo/sysinfo_test.go
new file mode 100644
index 000000000..b61fbcf54
--- /dev/null
+++ b/pkg/sysinfo/sysinfo_test.go
@@ -0,0 +1,26 @@
+package sysinfo
+
+import "testing"
+
+func TestIsCpusetListAvailable(t *testing.T) {
+ cases := []struct {
+ provided string
+ available string
+ res bool
+ err bool
+ }{
+ {"1", "0-4", true, false},
+ {"01,3", "0-4", true, false},
+ {"", "0-7", true, false},
+ {"1--42", "0-7", false, true},
+ {"1-42", "00-1,8,,9", false, true},
+ {"1,41-42", "43,45", false, false},
+ {"0-3", "", false, false},
+ }
+ for _, c := range cases {
+ r, err := isCpusetListAvailable(c.provided, c.available)
+ if (c.err && err == nil) && r != c.res {
+ t.Fatalf("Expected pair: %v, %v for %s, %s. Got %v, %v instead", c.res, c.err, c.provided, c.available, (c.err && err == nil), r)
+ }
+ }
+}
diff --git a/pkg/sysinfo/sysinfo_unix.go b/pkg/sysinfo/sysinfo_unix.go
new file mode 100644
index 000000000..45f3ef1c6
--- /dev/null
+++ b/pkg/sysinfo/sysinfo_unix.go
@@ -0,0 +1,9 @@
+// +build !linux,!solaris,!windows
+
+package sysinfo
+
+// New returns an empty SysInfo for non linux nor solaris for now.
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ return sysInfo
+}
diff --git a/pkg/sysinfo/sysinfo_windows.go b/pkg/sysinfo/sysinfo_windows.go
new file mode 100644
index 000000000..4e6255bc5
--- /dev/null
+++ b/pkg/sysinfo/sysinfo_windows.go
@@ -0,0 +1,9 @@
+// +build windows
+
+package sysinfo
+
+// New returns an empty SysInfo for windows for now.
+func New(quiet bool) *SysInfo {
+ sysInfo := &SysInfo{}
+ return sysInfo
+}