github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/pkg/sysinfo/sysinfo_linux.go (about)

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