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 }