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 }