github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/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  	"sync"
     9  
    10  	cdcgroups "github.com/containerd/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  type infoCollector func(info *SysInfo, cgMounts map[string]string) (warnings []string)
    31  
    32  type opts struct {
    33  	cg2GroupPath string
    34  }
    35  
    36  // Opt for New().
    37  type Opt func(*opts)
    38  
    39  // WithCgroup2GroupPath specifies the cgroup v2 group path to inspect availability
    40  // of the controllers.
    41  //
    42  // WithCgroup2GroupPath is expected to be used for rootless mode with systemd driver.
    43  //
    44  // e.g. g = "/user.slice/user-1000.slice/user@1000.service"
    45  func WithCgroup2GroupPath(g string) Opt {
    46  	return func(o *opts) {
    47  		o.cg2GroupPath = path.Clean(g)
    48  	}
    49  }
    50  
    51  // New returns a new SysInfo, using the filesystem to detect which features
    52  // the kernel supports. If `quiet` is `false` warnings are printed in logs
    53  // whenever an error occurs or misconfigurations are present.
    54  func New(quiet bool, options ...Opt) *SysInfo {
    55  	var opts opts
    56  	for _, o := range options {
    57  		o(&opts)
    58  	}
    59  	if cdcgroups.Mode() == cdcgroups.Unified {
    60  		return newV2(quiet, &opts)
    61  	}
    62  
    63  	var ops []infoCollector
    64  	var warnings []string
    65  	sysInfo := &SysInfo{}
    66  	cgMounts, err := findCgroupMountpoints()
    67  	if err != nil {
    68  		logrus.Warn(err)
    69  	} else {
    70  		ops = append(ops, []infoCollector{
    71  			applyMemoryCgroupInfo,
    72  			applyCPUCgroupInfo,
    73  			applyBlkioCgroupInfo,
    74  			applyCPUSetCgroupInfo,
    75  			applyPIDSCgroupInfo,
    76  			applyDevicesCgroupInfo,
    77  		}...)
    78  	}
    79  
    80  	ops = append(ops, []infoCollector{
    81  		applyNetworkingInfo,
    82  		applyAppArmorInfo,
    83  		applySeccompInfo,
    84  		applyCgroupNsInfo,
    85  	}...)
    86  
    87  	for _, o := range ops {
    88  		w := o(sysInfo, cgMounts)
    89  		warnings = append(warnings, w...)
    90  	}
    91  	if !quiet {
    92  		for _, w := range warnings {
    93  			logrus.Warn(w)
    94  		}
    95  	}
    96  	return sysInfo
    97  }
    98  
    99  // applyMemoryCgroupInfo adds the memory cgroup controller information to the info.
   100  func applyMemoryCgroupInfo(info *SysInfo, cgMounts map[string]string) []string {
   101  	var warnings []string
   102  	mountPoint, ok := cgMounts["memory"]
   103  	if !ok {
   104  		warnings = append(warnings, "Your kernel does not support cgroup memory limit")
   105  		return warnings
   106  	}
   107  	info.MemoryLimit = ok
   108  
   109  	info.SwapLimit = cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
   110  	if !info.SwapLimit {
   111  		warnings = append(warnings, "Your kernel does not support swap memory limit")
   112  	}
   113  	info.MemoryReservation = cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
   114  	if !info.MemoryReservation {
   115  		warnings = append(warnings, "Your kernel does not support memory reservation")
   116  	}
   117  	info.OomKillDisable = cgroupEnabled(mountPoint, "memory.oom_control")
   118  	if !info.OomKillDisable {
   119  		warnings = append(warnings, "Your kernel does not support oom control")
   120  	}
   121  	info.MemorySwappiness = cgroupEnabled(mountPoint, "memory.swappiness")
   122  	if !info.MemorySwappiness {
   123  		warnings = append(warnings, "Your kernel does not support memory swappiness")
   124  	}
   125  	info.KernelMemory = cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
   126  	if !info.KernelMemory {
   127  		warnings = append(warnings, "Your kernel does not support kernel memory limit")
   128  	}
   129  	info.KernelMemoryTCP = cgroupEnabled(mountPoint, "memory.kmem.tcp.limit_in_bytes")
   130  	if !info.KernelMemoryTCP {
   131  		warnings = append(warnings, "Your kernel does not support kernel memory TCP limit")
   132  	}
   133  
   134  	return warnings
   135  }
   136  
   137  // applyCPUCgroupInfo adds the cpu cgroup controller information to the info.
   138  func applyCPUCgroupInfo(info *SysInfo, cgMounts map[string]string) []string {
   139  	var warnings []string
   140  	mountPoint, ok := cgMounts["cpu"]
   141  	if !ok {
   142  		warnings = append(warnings, "Unable to find cpu cgroup in mounts")
   143  		return warnings
   144  	}
   145  
   146  	info.CPUShares = cgroupEnabled(mountPoint, "cpu.shares")
   147  	if !info.CPUShares {
   148  		warnings = append(warnings, "Your kernel does not support CPU shares")
   149  	}
   150  
   151  	info.CPUCfs = cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
   152  	if !info.CPUCfs {
   153  		warnings = append(warnings, "Your kernel does not support CPU CFS scheduler")
   154  	}
   155  
   156  	info.CPURealtime = cgroupEnabled(mountPoint, "cpu.rt_period_us")
   157  	if !info.CPURealtime {
   158  		warnings = append(warnings, "Your kernel does not support CPU realtime scheduler")
   159  	}
   160  
   161  	return warnings
   162  }
   163  
   164  // applyBlkioCgroupInfo adds the blkio cgroup controller information to the info.
   165  func applyBlkioCgroupInfo(info *SysInfo, cgMounts map[string]string) []string {
   166  	var warnings []string
   167  	mountPoint, ok := cgMounts["blkio"]
   168  	if !ok {
   169  		warnings = append(warnings, "Unable to find blkio cgroup in mounts")
   170  		return warnings
   171  	}
   172  
   173  	info.BlkioWeight = cgroupEnabled(mountPoint, "blkio.weight")
   174  	if !info.BlkioWeight {
   175  		warnings = append(warnings, "Your kernel does not support cgroup blkio weight")
   176  	}
   177  
   178  	info.BlkioWeightDevice = cgroupEnabled(mountPoint, "blkio.weight_device")
   179  	if !info.BlkioWeightDevice {
   180  		warnings = append(warnings, "Your kernel does not support cgroup blkio weight_device")
   181  	}
   182  
   183  	info.BlkioReadBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
   184  	if !info.BlkioReadBpsDevice {
   185  		warnings = append(warnings, "Your kernel does not support cgroup blkio throttle.read_bps_device")
   186  	}
   187  
   188  	info.BlkioWriteBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
   189  	if !info.BlkioWriteBpsDevice {
   190  		warnings = append(warnings, "Your kernel does not support cgroup blkio throttle.write_bps_device")
   191  	}
   192  	info.BlkioReadIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
   193  	if !info.BlkioReadIOpsDevice {
   194  		warnings = append(warnings, "Your kernel does not support cgroup blkio throttle.read_iops_device")
   195  	}
   196  
   197  	info.BlkioWriteIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
   198  	if !info.BlkioWriteIOpsDevice {
   199  		warnings = append(warnings, "Your kernel does not support cgroup blkio throttle.write_iops_device")
   200  	}
   201  
   202  	return warnings
   203  }
   204  
   205  // applyCPUSetCgroupInfo adds the cpuset cgroup controller information to the info.
   206  func applyCPUSetCgroupInfo(info *SysInfo, cgMounts map[string]string) []string {
   207  	var warnings []string
   208  	mountPoint, ok := cgMounts["cpuset"]
   209  	if !ok {
   210  		warnings = append(warnings, "Unable to find cpuset cgroup in mounts")
   211  		return warnings
   212  	}
   213  	info.Cpuset = ok
   214  
   215  	var err error
   216  
   217  	cpus, err := os.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
   218  	if err != nil {
   219  		return warnings
   220  	}
   221  	info.Cpus = strings.TrimSpace(string(cpus))
   222  
   223  	mems, err := os.ReadFile(path.Join(mountPoint, "cpuset.mems"))
   224  	if err != nil {
   225  		return warnings
   226  	}
   227  	info.Mems = strings.TrimSpace(string(mems))
   228  
   229  	return warnings
   230  }
   231  
   232  // applyPIDSCgroupInfo adds whether the pids cgroup controller is available to the info.
   233  func applyPIDSCgroupInfo(info *SysInfo, cgMounts map[string]string) []string {
   234  	var warnings []string
   235  	_, ok := cgMounts["pids"]
   236  	if !ok {
   237  		warnings = append(warnings, "Unable to find pids cgroup in mounts")
   238  		return warnings
   239  	}
   240  	info.PidsLimit = true
   241  	return warnings
   242  }
   243  
   244  // applyDevicesCgroupInfo adds whether the devices cgroup controller is available to the info.
   245  func applyDevicesCgroupInfo(info *SysInfo, cgMounts map[string]string) []string {
   246  	var warnings []string
   247  	_, ok := cgMounts["devices"]
   248  	info.CgroupDevicesEnabled = ok
   249  	return warnings
   250  }
   251  
   252  // applyNetworkingInfo adds networking information to the info.
   253  func applyNetworkingInfo(info *SysInfo, _ map[string]string) []string {
   254  	var warnings []string
   255  	info.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
   256  	info.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
   257  	info.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
   258  	return warnings
   259  }
   260  
   261  // applyAppArmorInfo adds whether AppArmor is enabled to the info.
   262  func applyAppArmorInfo(info *SysInfo, _ map[string]string) []string {
   263  	var warnings []string
   264  	if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
   265  		if _, err := os.ReadFile("/sys/kernel/security/apparmor/profiles"); err == nil {
   266  			info.AppArmor = true
   267  		}
   268  	}
   269  	return warnings
   270  }
   271  
   272  // applyCgroupNsInfo adds whether cgroupns is enabled to the info.
   273  func applyCgroupNsInfo(info *SysInfo, _ map[string]string) []string {
   274  	var warnings []string
   275  	if _, err := os.Stat("/proc/self/ns/cgroup"); !os.IsNotExist(err) {
   276  		info.CgroupNamespaces = true
   277  	}
   278  	return warnings
   279  }
   280  
   281  var (
   282  	seccompOnce    sync.Once
   283  	seccompEnabled bool
   284  )
   285  
   286  // applySeccompInfo checks if Seccomp is supported, via CONFIG_SECCOMP.
   287  func applySeccompInfo(info *SysInfo, _ map[string]string) []string {
   288  	var warnings []string
   289  	seccompOnce.Do(func() {
   290  		// Check if Seccomp is supported, via CONFIG_SECCOMP.
   291  		if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
   292  			// Make sure the kernel has CONFIG_SECCOMP_FILTER.
   293  			if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
   294  				seccompEnabled = true
   295  			}
   296  		}
   297  	})
   298  	info.Seccomp = seccompEnabled
   299  	return warnings
   300  }
   301  
   302  func cgroupEnabled(mountPoint, name string) bool {
   303  	_, err := os.Stat(path.Join(mountPoint, name))
   304  	return err == nil
   305  }
   306  
   307  func readProcBool(path string) bool {
   308  	val, err := os.ReadFile(path)
   309  	if err != nil {
   310  		return false
   311  	}
   312  	return strings.TrimSpace(string(val)) == "1"
   313  }