sysinfo_linux.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path"
  7. "strings"
  8. "sync"
  9. cdcgroups "github.com/containerd/cgroups"
  10. "github.com/opencontainers/runc/libcontainer/cgroups"
  11. "github.com/sirupsen/logrus"
  12. "golang.org/x/sys/unix"
  13. )
  14. func findCgroupMountpoints() (map[string]string, error) {
  15. cgMounts, err := cgroups.GetCgroupMounts(false)
  16. if err != nil {
  17. return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
  18. }
  19. mps := make(map[string]string)
  20. for _, m := range cgMounts {
  21. for _, ss := range m.Subsystems {
  22. mps[ss] = m.Mountpoint
  23. }
  24. }
  25. return mps, nil
  26. }
  27. type infoCollector func(info *SysInfo)
  28. // WithCgroup2GroupPath specifies the cgroup v2 group path to inspect availability
  29. // of the controllers.
  30. //
  31. // WithCgroup2GroupPath is expected to be used for rootless mode with systemd driver.
  32. //
  33. // e.g. g = "/user.slice/user-1000.slice/user@1000.service"
  34. func WithCgroup2GroupPath(g string) Opt {
  35. return func(o *SysInfo) {
  36. if p := path.Clean(g); p != "" {
  37. o.cg2GroupPath = p
  38. }
  39. }
  40. }
  41. // New returns a new SysInfo, using the filesystem to detect which features
  42. // the kernel supports.
  43. func New(options ...Opt) *SysInfo {
  44. if cdcgroups.Mode() == cdcgroups.Unified {
  45. return newV2(options...)
  46. }
  47. return newV1()
  48. }
  49. func newV1() *SysInfo {
  50. var (
  51. err error
  52. sysInfo = &SysInfo{}
  53. )
  54. ops := []infoCollector{
  55. applyNetworkingInfo,
  56. applyAppArmorInfo,
  57. applySeccompInfo,
  58. applyCgroupNsInfo,
  59. }
  60. sysInfo.cgMounts, err = findCgroupMountpoints()
  61. if err != nil {
  62. logrus.Warn(err)
  63. } else {
  64. ops = append(ops,
  65. applyMemoryCgroupInfo,
  66. applyCPUCgroupInfo,
  67. applyBlkioCgroupInfo,
  68. applyCPUSetCgroupInfo,
  69. applyPIDSCgroupInfo,
  70. applyDevicesCgroupInfo,
  71. )
  72. }
  73. for _, o := range ops {
  74. o(sysInfo)
  75. }
  76. return sysInfo
  77. }
  78. // applyMemoryCgroupInfo adds the memory cgroup controller information to the info.
  79. func applyMemoryCgroupInfo(info *SysInfo) {
  80. mountPoint, ok := info.cgMounts["memory"]
  81. if !ok {
  82. info.Warnings = append(info.Warnings, "Your kernel does not support cgroup memory limit")
  83. return
  84. }
  85. info.MemoryLimit = ok
  86. info.SwapLimit = cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
  87. if !info.SwapLimit {
  88. info.Warnings = append(info.Warnings, "Your kernel does not support swap memory limit")
  89. }
  90. info.MemoryReservation = cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
  91. if !info.MemoryReservation {
  92. info.Warnings = append(info.Warnings, "Your kernel does not support memory reservation")
  93. }
  94. info.OomKillDisable = cgroupEnabled(mountPoint, "memory.oom_control")
  95. if !info.OomKillDisable {
  96. info.Warnings = append(info.Warnings, "Your kernel does not support oom control")
  97. }
  98. info.MemorySwappiness = cgroupEnabled(mountPoint, "memory.swappiness")
  99. if !info.MemorySwappiness {
  100. info.Warnings = append(info.Warnings, "Your kernel does not support memory swappiness")
  101. }
  102. info.KernelMemory = cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
  103. if !info.KernelMemory {
  104. info.Warnings = append(info.Warnings, "Your kernel does not support kernel memory limit")
  105. }
  106. info.KernelMemoryTCP = cgroupEnabled(mountPoint, "memory.kmem.tcp.limit_in_bytes")
  107. if !info.KernelMemoryTCP {
  108. info.Warnings = append(info.Warnings, "Your kernel does not support kernel memory TCP limit")
  109. }
  110. }
  111. // applyCPUCgroupInfo adds the cpu cgroup controller information to the info.
  112. func applyCPUCgroupInfo(info *SysInfo) {
  113. mountPoint, ok := info.cgMounts["cpu"]
  114. if !ok {
  115. info.Warnings = append(info.Warnings, "Unable to find cpu cgroup in mounts")
  116. return
  117. }
  118. info.CPUShares = cgroupEnabled(mountPoint, "cpu.shares")
  119. if !info.CPUShares {
  120. info.Warnings = append(info.Warnings, "Your kernel does not support CPU shares")
  121. }
  122. info.CPUCfs = cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
  123. if !info.CPUCfs {
  124. info.Warnings = append(info.Warnings, "Your kernel does not support CPU CFS scheduler")
  125. }
  126. info.CPURealtime = cgroupEnabled(mountPoint, "cpu.rt_period_us")
  127. if !info.CPURealtime {
  128. info.Warnings = append(info.Warnings, "Your kernel does not support CPU realtime scheduler")
  129. }
  130. }
  131. // applyBlkioCgroupInfo adds the blkio cgroup controller information to the info.
  132. func applyBlkioCgroupInfo(info *SysInfo) {
  133. mountPoint, ok := info.cgMounts["blkio"]
  134. if !ok {
  135. info.Warnings = append(info.Warnings, "Unable to find blkio cgroup in mounts")
  136. return
  137. }
  138. info.BlkioWeight = cgroupEnabled(mountPoint, "blkio.weight")
  139. if !info.BlkioWeight {
  140. info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio weight")
  141. }
  142. info.BlkioWeightDevice = cgroupEnabled(mountPoint, "blkio.weight_device")
  143. if !info.BlkioWeightDevice {
  144. info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio weight_device")
  145. }
  146. info.BlkioReadBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
  147. if !info.BlkioReadBpsDevice {
  148. info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.read_bps_device")
  149. }
  150. info.BlkioWriteBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
  151. if !info.BlkioWriteBpsDevice {
  152. info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.write_bps_device")
  153. }
  154. info.BlkioReadIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
  155. if !info.BlkioReadIOpsDevice {
  156. info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.read_iops_device")
  157. }
  158. info.BlkioWriteIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
  159. if !info.BlkioWriteIOpsDevice {
  160. info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.write_iops_device")
  161. }
  162. }
  163. // applyCPUSetCgroupInfo adds the cpuset cgroup controller information to the info.
  164. func applyCPUSetCgroupInfo(info *SysInfo) {
  165. mountPoint, ok := info.cgMounts["cpuset"]
  166. if !ok {
  167. info.Warnings = append(info.Warnings, "Unable to find cpuset cgroup in mounts")
  168. return
  169. }
  170. info.Cpuset = ok
  171. var err error
  172. cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
  173. if err != nil {
  174. return
  175. }
  176. info.Cpus = strings.TrimSpace(string(cpus))
  177. mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
  178. if err != nil {
  179. return
  180. }
  181. info.Mems = strings.TrimSpace(string(mems))
  182. }
  183. // applyPIDSCgroupInfo adds whether the pids cgroup controller is available to the info.
  184. func applyPIDSCgroupInfo(info *SysInfo) {
  185. _, ok := info.cgMounts["pids"]
  186. if !ok {
  187. info.Warnings = append(info.Warnings, "Unable to find pids cgroup in mounts")
  188. return
  189. }
  190. info.PidsLimit = true
  191. }
  192. // applyDevicesCgroupInfo adds whether the devices cgroup controller is available to the info.
  193. func applyDevicesCgroupInfo(info *SysInfo) {
  194. _, ok := info.cgMounts["devices"]
  195. info.CgroupDevicesEnabled = ok
  196. }
  197. // applyNetworkingInfo adds networking information to the info.
  198. func applyNetworkingInfo(info *SysInfo) {
  199. info.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
  200. info.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
  201. info.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
  202. }
  203. // applyAppArmorInfo adds whether AppArmor is enabled to the info.
  204. func applyAppArmorInfo(info *SysInfo) {
  205. if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
  206. if _, err := ioutil.ReadFile("/sys/kernel/security/apparmor/profiles"); err == nil {
  207. info.AppArmor = true
  208. }
  209. }
  210. }
  211. // applyCgroupNsInfo adds whether cgroupns is enabled to the info.
  212. func applyCgroupNsInfo(info *SysInfo) {
  213. if _, err := os.Stat("/proc/self/ns/cgroup"); !os.IsNotExist(err) {
  214. info.CgroupNamespaces = true
  215. }
  216. }
  217. var (
  218. seccompOnce sync.Once
  219. seccompEnabled bool
  220. )
  221. // applySeccompInfo checks if Seccomp is supported, via CONFIG_SECCOMP.
  222. func applySeccompInfo(info *SysInfo) {
  223. seccompOnce.Do(func() {
  224. // Check if Seccomp is supported, via CONFIG_SECCOMP.
  225. if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
  226. // Make sure the kernel has CONFIG_SECCOMP_FILTER.
  227. if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
  228. seccompEnabled = true
  229. }
  230. }
  231. })
  232. info.Seccomp = seccompEnabled
  233. }
  234. func cgroupEnabled(mountPoint, name string) bool {
  235. _, err := os.Stat(path.Join(mountPoint, name))
  236. return err == nil
  237. }
  238. func readProcBool(path string) bool {
  239. val, err := ioutil.ReadFile(path)
  240. if err != nil {
  241. return false
  242. }
  243. return strings.TrimSpace(string(val)) == "1"
  244. }