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