github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/pkg/sysinfo/sysinfo_linux.go (about)

     1  package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path"
     7  	"strings"
     8  
     9  	cdcgroups "github.com/containerd/cgroups"
    10  	cdseccomp "github.com/containerd/containerd/pkg/seccomp"
    11  	"github.com/opencontainers/runc/libcontainer/cgroups"
    12  	"github.com/sirupsen/logrus"
    13  )
    14  
    15  func findCgroupMountpoints() (map[string]string, error) {
    16  	cgMounts, err := cgroups.GetCgroupMounts(false)
    17  	if err != nil {
    18  		return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
    19  	}
    20  	mps := make(map[string]string)
    21  	for _, m := range cgMounts {
    22  		for _, ss := range m.Subsystems {
    23  			mps[ss] = m.Mountpoint
    24  		}
    25  	}
    26  	return mps, nil
    27  }
    28  
    29  type infoCollector func(info *SysInfo)
    30  
    31  // WithCgroup2GroupPath specifies the cgroup v2 group path to inspect availability
    32  // of the controllers.
    33  //
    34  // WithCgroup2GroupPath is expected to be used for rootless mode with systemd driver.
    35  //
    36  // e.g. g = "/user.slice/user-1000.slice/user@1000.service"
    37  func WithCgroup2GroupPath(g string) Opt {
    38  	return func(o *SysInfo) {
    39  		if p := path.Clean(g); p != "" {
    40  			o.cg2GroupPath = p
    41  		}
    42  	}
    43  }
    44  
    45  // New returns a new SysInfo, using the filesystem to detect which features
    46  // the kernel supports.
    47  func New(options ...Opt) *SysInfo {
    48  	if cdcgroups.Mode() == cdcgroups.Unified {
    49  		return newV2(options...)
    50  	}
    51  	return newV1()
    52  }
    53  
    54  func newV1() *SysInfo {
    55  	var (
    56  		err     error
    57  		sysInfo = &SysInfo{}
    58  	)
    59  
    60  	ops := []infoCollector{
    61  		applyNetworkingInfo,
    62  		applyAppArmorInfo,
    63  		applySeccompInfo,
    64  		applyCgroupNsInfo,
    65  	}
    66  
    67  	sysInfo.cgMounts, err = findCgroupMountpoints()
    68  	if err != nil {
    69  		logrus.Warn(err)
    70  	} else {
    71  		ops = append(ops,
    72  			applyMemoryCgroupInfo,
    73  			applyCPUCgroupInfo,
    74  			applyBlkioCgroupInfo,
    75  			applyCPUSetCgroupInfo,
    76  			applyPIDSCgroupInfo,
    77  			applyDevicesCgroupInfo,
    78  		)
    79  	}
    80  
    81  	for _, o := range ops {
    82  		o(sysInfo)
    83  	}
    84  	return sysInfo
    85  }
    86  
    87  // applyMemoryCgroupInfo adds the memory cgroup controller information to the info.
    88  func applyMemoryCgroupInfo(info *SysInfo) {
    89  	mountPoint, ok := info.cgMounts["memory"]
    90  	if !ok {
    91  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup memory limit")
    92  		return
    93  	}
    94  	info.MemoryLimit = ok
    95  
    96  	info.SwapLimit = cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
    97  	if !info.SwapLimit {
    98  		info.Warnings = append(info.Warnings, "Your kernel does not support swap memory limit")
    99  	}
   100  	info.MemoryReservation = cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
   101  	if !info.MemoryReservation {
   102  		info.Warnings = append(info.Warnings, "Your kernel does not support memory reservation")
   103  	}
   104  	info.OomKillDisable = cgroupEnabled(mountPoint, "memory.oom_control")
   105  	if !info.OomKillDisable {
   106  		info.Warnings = append(info.Warnings, "Your kernel does not support oom control")
   107  	}
   108  	info.MemorySwappiness = cgroupEnabled(mountPoint, "memory.swappiness")
   109  	if !info.MemorySwappiness {
   110  		info.Warnings = append(info.Warnings, "Your kernel does not support memory swappiness")
   111  	}
   112  	info.KernelMemory = cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
   113  	if !info.KernelMemory {
   114  		info.Warnings = append(info.Warnings, "Your kernel does not support kernel memory limit")
   115  	}
   116  	info.KernelMemoryTCP = cgroupEnabled(mountPoint, "memory.kmem.tcp.limit_in_bytes")
   117  	if !info.KernelMemoryTCP {
   118  		info.Warnings = append(info.Warnings, "Your kernel does not support kernel memory TCP limit")
   119  	}
   120  }
   121  
   122  // applyCPUCgroupInfo adds the cpu cgroup controller information to the info.
   123  func applyCPUCgroupInfo(info *SysInfo) {
   124  	mountPoint, ok := info.cgMounts["cpu"]
   125  	if !ok {
   126  		info.Warnings = append(info.Warnings, "Unable to find cpu cgroup in mounts")
   127  		return
   128  	}
   129  
   130  	info.CPUShares = cgroupEnabled(mountPoint, "cpu.shares")
   131  	if !info.CPUShares {
   132  		info.Warnings = append(info.Warnings, "Your kernel does not support CPU shares")
   133  	}
   134  
   135  	info.CPUCfs = cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
   136  	if !info.CPUCfs {
   137  		info.Warnings = append(info.Warnings, "Your kernel does not support CPU CFS scheduler")
   138  	}
   139  
   140  	info.CPURealtime = cgroupEnabled(mountPoint, "cpu.rt_period_us")
   141  	if !info.CPURealtime {
   142  		info.Warnings = append(info.Warnings, "Your kernel does not support CPU realtime scheduler")
   143  	}
   144  }
   145  
   146  // applyBlkioCgroupInfo adds the blkio cgroup controller information to the info.
   147  func applyBlkioCgroupInfo(info *SysInfo) {
   148  	mountPoint, ok := info.cgMounts["blkio"]
   149  	if !ok {
   150  		info.Warnings = append(info.Warnings, "Unable to find blkio cgroup in mounts")
   151  		return
   152  	}
   153  
   154  	info.BlkioWeight = cgroupEnabled(mountPoint, "blkio.weight")
   155  	if !info.BlkioWeight {
   156  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio weight")
   157  	}
   158  
   159  	info.BlkioWeightDevice = cgroupEnabled(mountPoint, "blkio.weight_device")
   160  	if !info.BlkioWeightDevice {
   161  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio weight_device")
   162  	}
   163  
   164  	info.BlkioReadBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
   165  	if !info.BlkioReadBpsDevice {
   166  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.read_bps_device")
   167  	}
   168  
   169  	info.BlkioWriteBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
   170  	if !info.BlkioWriteBpsDevice {
   171  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.write_bps_device")
   172  	}
   173  	info.BlkioReadIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
   174  	if !info.BlkioReadIOpsDevice {
   175  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.read_iops_device")
   176  	}
   177  
   178  	info.BlkioWriteIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
   179  	if !info.BlkioWriteIOpsDevice {
   180  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.write_iops_device")
   181  	}
   182  }
   183  
   184  // applyCPUSetCgroupInfo adds the cpuset cgroup controller information to the info.
   185  func applyCPUSetCgroupInfo(info *SysInfo) {
   186  	mountPoint, ok := info.cgMounts["cpuset"]
   187  	if !ok {
   188  		info.Warnings = append(info.Warnings, "Unable to find cpuset cgroup in mounts")
   189  		return
   190  	}
   191  	info.Cpuset = ok
   192  
   193  	var err error
   194  
   195  	cpus, err := os.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
   196  	if err != nil {
   197  		return
   198  	}
   199  	info.Cpus = strings.TrimSpace(string(cpus))
   200  
   201  	mems, err := os.ReadFile(path.Join(mountPoint, "cpuset.mems"))
   202  	if err != nil {
   203  		return
   204  	}
   205  	info.Mems = strings.TrimSpace(string(mems))
   206  }
   207  
   208  // applyPIDSCgroupInfo adds whether the pids cgroup controller is available to the info.
   209  func applyPIDSCgroupInfo(info *SysInfo) {
   210  	_, ok := info.cgMounts["pids"]
   211  	if !ok {
   212  		info.Warnings = append(info.Warnings, "Unable to find pids cgroup in mounts")
   213  		return
   214  	}
   215  	info.PidsLimit = true
   216  }
   217  
   218  // applyDevicesCgroupInfo adds whether the devices cgroup controller is available to the info.
   219  func applyDevicesCgroupInfo(info *SysInfo) {
   220  	_, ok := info.cgMounts["devices"]
   221  	info.CgroupDevicesEnabled = ok
   222  }
   223  
   224  // applyNetworkingInfo adds networking information to the info.
   225  func applyNetworkingInfo(info *SysInfo) {
   226  	info.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
   227  	info.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
   228  	info.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
   229  }
   230  
   231  // applyAppArmorInfo adds whether AppArmor is enabled to the info.
   232  func applyAppArmorInfo(info *SysInfo) {
   233  	if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
   234  		if _, err := os.ReadFile("/sys/kernel/security/apparmor/profiles"); err == nil {
   235  			info.AppArmor = true
   236  		}
   237  	}
   238  }
   239  
   240  // applyCgroupNsInfo adds whether cgroupns is enabled to the info.
   241  func applyCgroupNsInfo(info *SysInfo) {
   242  	if _, err := os.Stat("/proc/self/ns/cgroup"); !os.IsNotExist(err) {
   243  		info.CgroupNamespaces = true
   244  	}
   245  }
   246  
   247  // applySeccompInfo checks if Seccomp is supported, via CONFIG_SECCOMP.
   248  func applySeccompInfo(info *SysInfo) {
   249  	info.Seccomp = cdseccomp.IsEnabled()
   250  }
   251  
   252  func cgroupEnabled(mountPoint, name string) bool {
   253  	_, err := os.Stat(path.Join(mountPoint, name))
   254  	return err == nil
   255  }
   256  
   257  func readProcBool(path string) bool {
   258  	val, err := os.ReadFile(path)
   259  	if err != nil {
   260  		return false
   261  	}
   262  	return strings.TrimSpace(string(val)) == "1"
   263  }