github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/pkg/sysinfo/sysinfo_linux.go (about)

     1  package sysinfo // import "github.com/Prakhar-Agarwal-byte/moby/pkg/sysinfo"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/containerd/cgroups/v3"
    12  	"github.com/containerd/cgroups/v3/cgroup1"
    13  	"github.com/containerd/containerd/pkg/seccomp"
    14  	"github.com/containerd/log"
    15  	"github.com/moby/sys/mountinfo"
    16  )
    17  
    18  var (
    19  	readMountinfoOnce sync.Once
    20  	readMountinfoErr  error
    21  	cgroupMountinfo   []*mountinfo.Info
    22  )
    23  
    24  // readCgroupMountinfo returns a list of cgroup v1 mounts (i.e. the ones
    25  // with fstype of "cgroup") for the current running process.
    26  //
    27  // The results are cached (to avoid re-reading mountinfo which is relatively
    28  // expensive), so it is assumed that cgroup mounts are not being changed.
    29  func readCgroupMountinfo() ([]*mountinfo.Info, error) {
    30  	readMountinfoOnce.Do(func() {
    31  		cgroupMountinfo, readMountinfoErr = mountinfo.GetMounts(
    32  			mountinfo.FSTypeFilter("cgroup"),
    33  		)
    34  	})
    35  
    36  	return cgroupMountinfo, readMountinfoErr
    37  }
    38  
    39  func findCgroupV1Mountpoints() (map[string]string, error) {
    40  	mounts, err := readCgroupMountinfo()
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	allSubsystems, err := cgroup1.ParseCgroupFile("/proc/self/cgroup")
    46  	if err != nil {
    47  		return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
    48  	}
    49  
    50  	allMap := make(map[string]bool)
    51  	for s := range allSubsystems {
    52  		allMap[s] = false
    53  	}
    54  
    55  	mps := make(map[string]string)
    56  	for _, mi := range mounts {
    57  		for _, opt := range strings.Split(mi.VFSOptions, ",") {
    58  			seen, known := allMap[opt]
    59  			if known && !seen {
    60  				allMap[opt] = true
    61  				mps[strings.TrimPrefix(opt, "name=")] = mi.Mountpoint
    62  			}
    63  		}
    64  		if len(mps) >= len(allMap) {
    65  			break
    66  		}
    67  	}
    68  	return mps, nil
    69  }
    70  
    71  type infoCollector func(info *SysInfo)
    72  
    73  // WithCgroup2GroupPath specifies the cgroup v2 group path to inspect availability
    74  // of the controllers.
    75  //
    76  // WithCgroup2GroupPath is expected to be used for rootless mode with systemd driver.
    77  //
    78  // e.g. g = "/user.slice/user-1000.slice/user@1000.service"
    79  func WithCgroup2GroupPath(g string) Opt {
    80  	return func(o *SysInfo) {
    81  		if p := path.Clean(g); p != "" {
    82  			o.cg2GroupPath = p
    83  		}
    84  	}
    85  }
    86  
    87  // New returns a new SysInfo, using the filesystem to detect which features
    88  // the kernel supports.
    89  func New(options ...Opt) *SysInfo {
    90  	if cgroups.Mode() == cgroups.Unified {
    91  		return newV2(options...)
    92  	}
    93  	return newV1()
    94  }
    95  
    96  func newV1() *SysInfo {
    97  	var (
    98  		err     error
    99  		sysInfo = &SysInfo{}
   100  	)
   101  
   102  	ops := []infoCollector{
   103  		applyNetworkingInfo,
   104  		applyAppArmorInfo,
   105  		applySeccompInfo,
   106  		applyCgroupNsInfo,
   107  	}
   108  
   109  	sysInfo.cgMounts, err = findCgroupV1Mountpoints()
   110  	if err != nil {
   111  		log.G(context.TODO()).Warn(err)
   112  	} else {
   113  		ops = append(ops,
   114  			applyMemoryCgroupInfo,
   115  			applyCPUCgroupInfo,
   116  			applyBlkioCgroupInfo,
   117  			applyCPUSetCgroupInfo,
   118  			applyPIDSCgroupInfo,
   119  			applyDevicesCgroupInfo,
   120  		)
   121  	}
   122  
   123  	for _, o := range ops {
   124  		o(sysInfo)
   125  	}
   126  	return sysInfo
   127  }
   128  
   129  // applyMemoryCgroupInfo adds the memory cgroup controller information to the info.
   130  func applyMemoryCgroupInfo(info *SysInfo) {
   131  	mountPoint, ok := info.cgMounts["memory"]
   132  	if !ok {
   133  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup memory limit")
   134  		return
   135  	}
   136  	info.MemoryLimit = ok
   137  
   138  	info.SwapLimit = cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
   139  	if !info.SwapLimit {
   140  		info.Warnings = append(info.Warnings, "Your kernel does not support swap memory limit")
   141  	}
   142  	info.MemoryReservation = cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
   143  	if !info.MemoryReservation {
   144  		info.Warnings = append(info.Warnings, "Your kernel does not support memory reservation")
   145  	}
   146  	info.OomKillDisable = cgroupEnabled(mountPoint, "memory.oom_control")
   147  	if !info.OomKillDisable {
   148  		info.Warnings = append(info.Warnings, "Your kernel does not support oom control")
   149  	}
   150  	info.MemorySwappiness = cgroupEnabled(mountPoint, "memory.swappiness")
   151  	if !info.MemorySwappiness {
   152  		info.Warnings = append(info.Warnings, "Your kernel does not support memory swappiness")
   153  	}
   154  
   155  	// Option is deprecated, but still accepted on API < v1.42 with cgroups v1,
   156  	// so setting the field to allow feature detection.
   157  	info.KernelMemory = cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
   158  
   159  	// Option is deprecated in runc, but still accepted in our API, so setting
   160  	// the field to allow feature detection, but don't warn if it's missing, to
   161  	// make the daemon logs a bit less noisy.
   162  	info.KernelMemoryTCP = cgroupEnabled(mountPoint, "memory.kmem.tcp.limit_in_bytes")
   163  }
   164  
   165  // applyCPUCgroupInfo adds the cpu cgroup controller information to the info.
   166  func applyCPUCgroupInfo(info *SysInfo) {
   167  	mountPoint, ok := info.cgMounts["cpu"]
   168  	if !ok {
   169  		info.Warnings = append(info.Warnings, "Unable to find cpu cgroup in mounts")
   170  		return
   171  	}
   172  
   173  	info.CPUShares = cgroupEnabled(mountPoint, "cpu.shares")
   174  	if !info.CPUShares {
   175  		info.Warnings = append(info.Warnings, "Your kernel does not support CPU shares")
   176  	}
   177  
   178  	info.CPUCfs = cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
   179  	if !info.CPUCfs {
   180  		info.Warnings = append(info.Warnings, "Your kernel does not support CPU CFS scheduler")
   181  	}
   182  
   183  	info.CPURealtime = cgroupEnabled(mountPoint, "cpu.rt_period_us")
   184  	if !info.CPURealtime {
   185  		info.Warnings = append(info.Warnings, "Your kernel does not support CPU realtime scheduler")
   186  	}
   187  }
   188  
   189  // applyBlkioCgroupInfo adds the blkio cgroup controller information to the info.
   190  func applyBlkioCgroupInfo(info *SysInfo) {
   191  	mountPoint, ok := info.cgMounts["blkio"]
   192  	if !ok {
   193  		info.Warnings = append(info.Warnings, "Unable to find blkio cgroup in mounts")
   194  		return
   195  	}
   196  
   197  	info.BlkioWeight = cgroupEnabled(mountPoint, "blkio.weight")
   198  	if !info.BlkioWeight {
   199  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio weight")
   200  	}
   201  
   202  	info.BlkioWeightDevice = cgroupEnabled(mountPoint, "blkio.weight_device")
   203  	if !info.BlkioWeightDevice {
   204  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio weight_device")
   205  	}
   206  
   207  	info.BlkioReadBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
   208  	if !info.BlkioReadBpsDevice {
   209  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.read_bps_device")
   210  	}
   211  
   212  	info.BlkioWriteBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
   213  	if !info.BlkioWriteBpsDevice {
   214  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.write_bps_device")
   215  	}
   216  	info.BlkioReadIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
   217  	if !info.BlkioReadIOpsDevice {
   218  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.read_iops_device")
   219  	}
   220  
   221  	info.BlkioWriteIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
   222  	if !info.BlkioWriteIOpsDevice {
   223  		info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.write_iops_device")
   224  	}
   225  }
   226  
   227  // applyCPUSetCgroupInfo adds the cpuset cgroup controller information to the info.
   228  func applyCPUSetCgroupInfo(info *SysInfo) {
   229  	mountPoint, ok := info.cgMounts["cpuset"]
   230  	if !ok {
   231  		info.Warnings = append(info.Warnings, "Unable to find cpuset cgroup in mounts")
   232  		return
   233  	}
   234  	info.Cpuset = ok
   235  
   236  	var err error
   237  
   238  	cpus, err := os.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
   239  	if err != nil {
   240  		return
   241  	}
   242  	info.Cpus = strings.TrimSpace(string(cpus))
   243  
   244  	mems, err := os.ReadFile(path.Join(mountPoint, "cpuset.mems"))
   245  	if err != nil {
   246  		return
   247  	}
   248  	info.Mems = strings.TrimSpace(string(mems))
   249  }
   250  
   251  // applyPIDSCgroupInfo adds whether the pids cgroup controller is available to the info.
   252  func applyPIDSCgroupInfo(info *SysInfo) {
   253  	_, ok := info.cgMounts["pids"]
   254  	if !ok {
   255  		info.Warnings = append(info.Warnings, "Unable to find pids cgroup in mounts")
   256  		return
   257  	}
   258  	info.PidsLimit = true
   259  }
   260  
   261  // applyDevicesCgroupInfo adds whether the devices cgroup controller is available to the info.
   262  func applyDevicesCgroupInfo(info *SysInfo) {
   263  	_, ok := info.cgMounts["devices"]
   264  	info.CgroupDevicesEnabled = ok
   265  }
   266  
   267  // applyNetworkingInfo adds networking information to the info.
   268  func applyNetworkingInfo(info *SysInfo) {
   269  	info.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
   270  	info.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
   271  	info.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
   272  }
   273  
   274  // applyAppArmorInfo adds whether AppArmor is enabled to the info.
   275  func applyAppArmorInfo(info *SysInfo) {
   276  	if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
   277  		if _, err := os.ReadFile("/sys/kernel/security/apparmor/profiles"); err == nil {
   278  			info.AppArmor = true
   279  		}
   280  	}
   281  }
   282  
   283  // applyCgroupNsInfo adds whether cgroupns is enabled to the info.
   284  func applyCgroupNsInfo(info *SysInfo) {
   285  	if _, err := os.Stat("/proc/self/ns/cgroup"); !os.IsNotExist(err) {
   286  		info.CgroupNamespaces = true
   287  	}
   288  }
   289  
   290  // applySeccompInfo checks if Seccomp is supported, via CONFIG_SECCOMP.
   291  func applySeccompInfo(info *SysInfo) {
   292  	info.Seccomp = seccomp.IsEnabled()
   293  }
   294  
   295  func cgroupEnabled(mountPoint, name string) bool {
   296  	_, err := os.Stat(path.Join(mountPoint, name))
   297  	return err == nil
   298  }
   299  
   300  func readProcBool(path string) bool {
   301  	val, err := os.ReadFile(path)
   302  	if err != nil {
   303  		return false
   304  	}
   305  	return strings.TrimSpace(string(val)) == "1"
   306  }