github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/sysinfo/sysinfo_linux.go (about)

     1  package sysinfo
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path"
     8  	"strings"
     9  
    10  	cg "github.com/containers/libpod/pkg/cgroups"
    11  	"github.com/opencontainers/runc/libcontainer/cgroups"
    12  	"github.com/sirupsen/logrus"
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  func findCgroupMountpoints() (map[string]string, error) {
    17  	cgMounts, err := cgroups.GetCgroupMounts(false)
    18  	if err != nil {
    19  		return nil, fmt.Errorf("failed to parse cgroup information: %v", err)
    20  	}
    21  	mps := make(map[string]string)
    22  	for _, m := range cgMounts {
    23  		for _, ss := range m.Subsystems {
    24  			mps[ss] = m.Mountpoint
    25  		}
    26  	}
    27  	return mps, nil
    28  }
    29  
    30  // New returns a new SysInfo, using the filesystem to detect which features
    31  // the kernel supports. If `quiet` is `false` warnings are printed in logs
    32  // whenever an error occurs or misconfigurations are present.
    33  func New(quiet bool) *SysInfo {
    34  	sysInfo := &SysInfo{}
    35  	cgMounts, err := findCgroupMountpoints()
    36  	if err != nil {
    37  		logrus.Warnf("Failed to parse cgroup information: %v", err)
    38  	} else {
    39  		sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
    40  		sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
    41  		sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
    42  		sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
    43  		sysInfo.cgroupPids = checkCgroupPids(quiet)
    44  	}
    45  
    46  	_, ok := cgMounts["devices"]
    47  	sysInfo.CgroupDevicesEnabled = ok
    48  
    49  	sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
    50  	sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
    51  	sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
    52  
    53  	// Check if AppArmor is supported.
    54  	if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
    55  		sysInfo.AppArmor = true
    56  	}
    57  
    58  	// Check if Seccomp is supported, via CONFIG_SECCOMP.
    59  	if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
    60  		// Make sure the kernel has CONFIG_SECCOMP_FILTER.
    61  		if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
    62  			sysInfo.Seccomp = true
    63  		}
    64  	}
    65  
    66  	return sysInfo
    67  }
    68  
    69  // checkCgroupMem reads the memory information from the memory cgroup mount point.
    70  func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
    71  	mountPoint, ok := cgMounts["memory"]
    72  	if !ok {
    73  		if !quiet {
    74  			logrus.Warn("Your kernel does not support cgroup memory limit")
    75  		}
    76  		return cgroupMemInfo{}
    77  	}
    78  
    79  	swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
    80  	if !quiet && !swapLimit {
    81  		logrus.Warn("Your kernel does not support swap memory limit")
    82  	}
    83  	memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
    84  	if !quiet && !memoryReservation {
    85  		logrus.Warn("Your kernel does not support memory reservation")
    86  	}
    87  	oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
    88  	if !quiet && !oomKillDisable {
    89  		logrus.Warn("Your kernel does not support oom control")
    90  	}
    91  	memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
    92  	if !quiet && !memorySwappiness {
    93  		logrus.Warn("Your kernel does not support memory swappiness")
    94  	}
    95  	kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
    96  	if !quiet && !kernelMemory {
    97  		logrus.Warn("Your kernel does not support kernel memory limit")
    98  	}
    99  
   100  	return cgroupMemInfo{
   101  		MemoryLimit:       true,
   102  		SwapLimit:         swapLimit,
   103  		MemoryReservation: memoryReservation,
   104  		OomKillDisable:    oomKillDisable,
   105  		MemorySwappiness:  memorySwappiness,
   106  		KernelMemory:      kernelMemory,
   107  	}
   108  }
   109  
   110  // checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
   111  func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
   112  	mountPoint, ok := cgMounts["cpu"]
   113  	if !ok {
   114  		if !quiet {
   115  			logrus.Warn("Unable to find cpu cgroup in mounts")
   116  		}
   117  		return cgroupCPUInfo{}
   118  	}
   119  
   120  	cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
   121  	if !quiet && !cpuShares {
   122  		logrus.Warn("Your kernel does not support cgroup cpu shares")
   123  	}
   124  
   125  	cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
   126  	if !quiet && !cpuCfsPeriod {
   127  		logrus.Warn("Your kernel does not support cgroup cfs period")
   128  	}
   129  
   130  	cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
   131  	if !quiet && !cpuCfsQuota {
   132  		logrus.Warn("Your kernel does not support cgroup cfs quotas")
   133  	}
   134  
   135  	cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
   136  	if !quiet && !cpuRealtimePeriod {
   137  		logrus.Warn("Your kernel does not support cgroup rt period")
   138  	}
   139  
   140  	cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
   141  	if !quiet && !cpuRealtimeRuntime {
   142  		logrus.Warn("Your kernel does not support cgroup rt runtime")
   143  	}
   144  
   145  	return cgroupCPUInfo{
   146  		CPUShares:          cpuShares,
   147  		CPUCfsPeriod:       cpuCfsPeriod,
   148  		CPUCfsQuota:        cpuCfsQuota,
   149  		CPURealtimePeriod:  cpuRealtimePeriod,
   150  		CPURealtimeRuntime: cpuRealtimeRuntime,
   151  	}
   152  }
   153  
   154  // checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
   155  func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
   156  	mountPoint, ok := cgMounts["blkio"]
   157  	if !ok {
   158  		if !quiet {
   159  			logrus.Warn("Unable to find blkio cgroup in mounts")
   160  		}
   161  		return cgroupBlkioInfo{}
   162  	}
   163  
   164  	weight := cgroupEnabled(mountPoint, "blkio.weight")
   165  	if !quiet && !weight {
   166  		logrus.Warn("Your kernel does not support cgroup blkio weight")
   167  	}
   168  
   169  	weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
   170  	if !quiet && !weightDevice {
   171  		logrus.Warn("Your kernel does not support cgroup blkio weight_device")
   172  	}
   173  
   174  	readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
   175  	if !quiet && !readBpsDevice {
   176  		logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
   177  	}
   178  
   179  	writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
   180  	if !quiet && !writeBpsDevice {
   181  		logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
   182  	}
   183  	readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
   184  	if !quiet && !readIOpsDevice {
   185  		logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
   186  	}
   187  
   188  	writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
   189  	if !quiet && !writeIOpsDevice {
   190  		logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
   191  	}
   192  	return cgroupBlkioInfo{
   193  		BlkioWeight:          weight,
   194  		BlkioWeightDevice:    weightDevice,
   195  		BlkioReadBpsDevice:   readBpsDevice,
   196  		BlkioWriteBpsDevice:  writeBpsDevice,
   197  		BlkioReadIOpsDevice:  readIOpsDevice,
   198  		BlkioWriteIOpsDevice: writeIOpsDevice,
   199  	}
   200  }
   201  
   202  // checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
   203  func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
   204  	mountPoint, ok := cgMounts["cpuset"]
   205  	if !ok {
   206  		if !quiet {
   207  			logrus.Warn("Unable to find cpuset cgroup in mounts")
   208  		}
   209  		return cgroupCpusetInfo{}
   210  	}
   211  
   212  	cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
   213  	if err != nil {
   214  		return cgroupCpusetInfo{}
   215  	}
   216  
   217  	mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
   218  	if err != nil {
   219  		return cgroupCpusetInfo{}
   220  	}
   221  
   222  	return cgroupCpusetInfo{
   223  		Cpuset: true,
   224  		Cpus:   strings.TrimSpace(string(cpus)),
   225  		Mems:   strings.TrimSpace(string(mems)),
   226  	}
   227  }
   228  
   229  // checkCgroupPids reads the pids information from the pids cgroup mount point.
   230  func checkCgroupPids(quiet bool) cgroupPids {
   231  	cgroup2, err := cg.IsCgroup2UnifiedMode()
   232  	if err != nil {
   233  		logrus.Errorf("Failed to check cgroups version: %v", err)
   234  	}
   235  	if !cgroup2 {
   236  		_, err := cgroups.FindCgroupMountpoint("", "pids")
   237  		if err != nil {
   238  			if !quiet {
   239  				logrus.Warn(err)
   240  			}
   241  			return cgroupPids{}
   242  		}
   243  	}
   244  
   245  	return cgroupPids{
   246  		PidsLimit: true,
   247  	}
   248  }
   249  
   250  func cgroupEnabled(mountPoint, name string) bool {
   251  	_, err := os.Stat(path.Join(mountPoint, name))
   252  	return err == nil
   253  }
   254  
   255  func readProcBool(path string) bool {
   256  	val, err := ioutil.ReadFile(path)
   257  	if err != nil {
   258  		return false
   259  	}
   260  	return strings.TrimSpace(string(val)) == "1"
   261  }