sysinfo_linux.go 10 KB

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