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 }