sysinfo_linux.go 9.1 KB

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