sysinfo_linux.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package sysinfo
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path"
  7. "strings"
  8. "syscall"
  9. "github.com/Sirupsen/logrus"
  10. "github.com/opencontainers/runc/libcontainer/cgroups"
  11. )
  12. const (
  13. // SeccompModeFilter refers to the syscall argument SECCOMP_MODE_FILTER.
  14. SeccompModeFilter = uintptr(2)
  15. )
  16. func findCgroupMountpoints() (map[string]string, error) {
  17. cgMounts, err := cgroups.GetCgroupMounts(false)
  18. if err != nil {
  19. return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
  20. }
  21. mps := make(map[string]string)
  22. for _, m := range cgMounts {
  23. for _, ss := range m.Subsystems {
  24. mps[ss] = m.Mountpoint
  25. }
  26. }
  27. return mps, nil
  28. }
  29. // New returns a new SysInfo, using the filesystem to detect which features
  30. // the kernel supports. If `quiet` is `false` warnings are printed in logs
  31. // whenever an error occurs or misconfigurations are present.
  32. func New(quiet bool) *SysInfo {
  33. sysInfo := &SysInfo{}
  34. cgMounts, err := findCgroupMountpoints()
  35. if err != nil {
  36. logrus.Warnf("Failed to parse cgroup information: %v", err)
  37. } else {
  38. sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
  39. sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
  40. sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
  41. sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
  42. sysInfo.cgroupPids = checkCgroupPids(quiet)
  43. }
  44. _, ok := cgMounts["devices"]
  45. sysInfo.CgroupDevicesEnabled = ok
  46. sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
  47. sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
  48. sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
  49. // Check if AppArmor is supported.
  50. if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
  51. sysInfo.AppArmor = true
  52. }
  53. // Check if Seccomp is supported, via CONFIG_SECCOMP.
  54. if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_SECCOMP, 0, 0); err != syscall.EINVAL {
  55. // Make sure the kernel has CONFIG_SECCOMP_FILTER.
  56. if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECCOMP, SeccompModeFilter, 0); err != syscall.EINVAL {
  57. sysInfo.Seccomp = true
  58. }
  59. }
  60. return sysInfo
  61. }
  62. // checkCgroupMem reads the memory information from the memory cgroup mount point.
  63. func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
  64. mountPoint, ok := cgMounts["memory"]
  65. if !ok {
  66. if !quiet {
  67. logrus.Warn("Your kernel does not support cgroup memory limit")
  68. }
  69. return cgroupMemInfo{}
  70. }
  71. swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
  72. if !quiet && !swapLimit {
  73. logrus.Warn("Your kernel does not support swap memory limit")
  74. }
  75. memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
  76. if !quiet && !memoryReservation {
  77. logrus.Warn("Your kernel does not support memory reservation")
  78. }
  79. oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
  80. if !quiet && !oomKillDisable {
  81. logrus.Warn("Your kernel does not support oom control")
  82. }
  83. memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
  84. if !quiet && !memorySwappiness {
  85. logrus.Warn("Your kernel does not support memory swappiness")
  86. }
  87. kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
  88. if !quiet && !kernelMemory {
  89. logrus.Warn("Your kernel does not support kernel memory limit")
  90. }
  91. return cgroupMemInfo{
  92. MemoryLimit: true,
  93. SwapLimit: swapLimit,
  94. MemoryReservation: memoryReservation,
  95. OomKillDisable: oomKillDisable,
  96. MemorySwappiness: memorySwappiness,
  97. KernelMemory: kernelMemory,
  98. }
  99. }
  100. // checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
  101. func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
  102. mountPoint, ok := cgMounts["cpu"]
  103. if !ok {
  104. if !quiet {
  105. logrus.Warn("Unable to find cpu cgroup in mounts")
  106. }
  107. return cgroupCPUInfo{}
  108. }
  109. cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
  110. if !quiet && !cpuShares {
  111. logrus.Warn("Your kernel does not support cgroup cpu shares")
  112. }
  113. cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
  114. if !quiet && !cpuCfsPeriod {
  115. logrus.Warn("Your kernel does not support cgroup cfs period")
  116. }
  117. cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
  118. if !quiet && !cpuCfsQuota {
  119. logrus.Warn("Your kernel does not support cgroup cfs quotas")
  120. }
  121. cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
  122. if !quiet && !cpuRealtimePeriod {
  123. logrus.Warn("Your kernel does not support cgroup rt period")
  124. }
  125. cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
  126. if !quiet && !cpuRealtimeRuntime {
  127. logrus.Warn("Your kernel does not support cgroup rt runtime")
  128. }
  129. return cgroupCPUInfo{
  130. CPUShares: cpuShares,
  131. CPUCfsPeriod: cpuCfsPeriod,
  132. CPUCfsQuota: cpuCfsQuota,
  133. CPURealtimePeriod: cpuRealtimePeriod,
  134. CPURealtimeRuntime: cpuRealtimeRuntime,
  135. }
  136. }
  137. // checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
  138. func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
  139. mountPoint, ok := cgMounts["blkio"]
  140. if !ok {
  141. if !quiet {
  142. logrus.Warn("Unable to find blkio cgroup in mounts")
  143. }
  144. return cgroupBlkioInfo{}
  145. }
  146. weight := cgroupEnabled(mountPoint, "blkio.weight")
  147. if !quiet && !weight {
  148. logrus.Warn("Your kernel does not support cgroup blkio weight")
  149. }
  150. weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
  151. if !quiet && !weightDevice {
  152. logrus.Warn("Your kernel does not support cgroup blkio weight_device")
  153. }
  154. readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
  155. if !quiet && !readBpsDevice {
  156. logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
  157. }
  158. writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
  159. if !quiet && !writeBpsDevice {
  160. logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
  161. }
  162. readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
  163. if !quiet && !readIOpsDevice {
  164. logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
  165. }
  166. writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
  167. if !quiet && !writeIOpsDevice {
  168. logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
  169. }
  170. return cgroupBlkioInfo{
  171. BlkioWeight: weight,
  172. BlkioWeightDevice: weightDevice,
  173. BlkioReadBpsDevice: readBpsDevice,
  174. BlkioWriteBpsDevice: writeBpsDevice,
  175. BlkioReadIOpsDevice: readIOpsDevice,
  176. BlkioWriteIOpsDevice: writeIOpsDevice,
  177. }
  178. }
  179. // checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
  180. func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
  181. mountPoint, ok := cgMounts["cpuset"]
  182. if !ok {
  183. if !quiet {
  184. logrus.Warn("Unable to find cpuset cgroup in mounts")
  185. }
  186. return cgroupCpusetInfo{}
  187. }
  188. cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
  189. if err != nil {
  190. return cgroupCpusetInfo{}
  191. }
  192. mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
  193. if err != nil {
  194. return cgroupCpusetInfo{}
  195. }
  196. return cgroupCpusetInfo{
  197. Cpuset: true,
  198. Cpus: strings.TrimSpace(string(cpus)),
  199. Mems: strings.TrimSpace(string(mems)),
  200. }
  201. }
  202. // checkCgroupPids reads the pids information from the pids cgroup mount point.
  203. func checkCgroupPids(quiet bool) cgroupPids {
  204. _, err := cgroups.FindCgroupMountpoint("pids")
  205. if err != nil {
  206. if !quiet {
  207. logrus.Warn(err)
  208. }
  209. return cgroupPids{}
  210. }
  211. return cgroupPids{
  212. PidsLimit: true,
  213. }
  214. }
  215. func cgroupEnabled(mountPoint, name string) bool {
  216. _, err := os.Stat(path.Join(mountPoint, name))
  217. return err == nil
  218. }
  219. func readProcBool(path string) bool {
  220. val, err := ioutil.ReadFile(path)
  221. if err != nil {
  222. return false
  223. }
  224. return strings.TrimSpace(string(val)) == "1"
  225. }