sys_linux.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. //go:build linux
  2. package sys
  3. import (
  4. "bufio"
  5. "fmt"
  6. "io"
  7. "os"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. "syscall"
  12. )
  13. var SIGUSR1 = syscall.SIGUSR1
  14. // countConnections returns the number of entries in a /proc/net/{tcp,udp}[6]
  15. // file. Returns 0 if the file is absent (e.g. /proc/net/tcp6 when IPv6 is
  16. // disabled) and excludes the column header line.
  17. func countConnections(path string) (int, error) {
  18. f, err := os.Open(path)
  19. if os.IsNotExist(err) {
  20. return 0, nil
  21. }
  22. if err != nil {
  23. return 0, err
  24. }
  25. defer f.Close()
  26. sc := bufio.NewScanner(f)
  27. n := 0
  28. for sc.Scan() {
  29. n++
  30. }
  31. if err := sc.Err(); err != nil {
  32. return 0, err
  33. }
  34. if n > 0 {
  35. n-- // first line is the column header
  36. }
  37. return n, nil
  38. }
  39. // GetTCPCount returns the number of active TCP connections by reading
  40. // /proc/net/tcp and /proc/net/tcp6 when available.
  41. func GetTCPCount() (int, error) {
  42. root := HostProc()
  43. tcp4, err := countConnections(root + "/net/tcp")
  44. if err != nil {
  45. return 0, err
  46. }
  47. tcp6, err := countConnections(root + "/net/tcp6")
  48. if err != nil {
  49. return 0, err
  50. }
  51. return tcp4 + tcp6, nil
  52. }
  53. // GetUDPCount returns the number of active UDP connections by reading
  54. // /proc/net/udp and /proc/net/udp6 when available.
  55. func GetUDPCount() (int, error) {
  56. root := HostProc()
  57. udp4, err := countConnections(root + "/net/udp")
  58. if err != nil {
  59. return 0, err
  60. }
  61. udp6, err := countConnections(root + "/net/udp6")
  62. if err != nil {
  63. return 0, err
  64. }
  65. return udp4 + udp6, nil
  66. }
  67. // --- CPU Utilization (Linux native) ---
  68. var (
  69. cpuMu sync.Mutex
  70. lastTotal uint64
  71. lastIdleAll uint64
  72. hasLast bool
  73. )
  74. // CPUPercentRaw returns instantaneous total CPU utilization by reading
  75. // /proc/stat. First call initializes and returns 0; subsequent calls return
  76. // busy/total * 100. Uses HostProc so HOST_PROC overrides (containers) apply.
  77. func CPUPercentRaw() (float64, error) {
  78. f, err := os.Open(HostProc("stat"))
  79. if err != nil {
  80. return 0, err
  81. }
  82. defer f.Close()
  83. rd := bufio.NewReader(f)
  84. line, err := rd.ReadString('\n')
  85. if err != nil && err != io.EOF {
  86. return 0, err
  87. }
  88. // Expect: cpu user nice system idle iowait irq softirq steal guest guest_nice
  89. fields := strings.Fields(line)
  90. if len(fields) < 5 || fields[0] != "cpu" {
  91. return 0, fmt.Errorf("unexpected /proc/stat format")
  92. }
  93. nums := make([]uint64, 0, len(fields)-1)
  94. for i := 1; i < len(fields); i++ {
  95. v, err := strconv.ParseUint(fields[i], 10, 64)
  96. if err != nil {
  97. break
  98. }
  99. nums = append(nums, v)
  100. }
  101. if len(nums) < 4 {
  102. return 0, fmt.Errorf("insufficient cpu fields")
  103. }
  104. for len(nums) < 8 {
  105. nums = append(nums, 0)
  106. }
  107. user, nice, system, idle := nums[0], nums[1], nums[2], nums[3]
  108. iowait, irq, softirq, steal := nums[4], nums[5], nums[6], nums[7]
  109. idleAll := idle + iowait
  110. nonIdle := user + nice + system + irq + softirq + steal
  111. total := idleAll + nonIdle
  112. cpuMu.Lock()
  113. defer cpuMu.Unlock()
  114. if !hasLast {
  115. lastTotal = total
  116. lastIdleAll = idleAll
  117. hasLast = true
  118. return 0, nil
  119. }
  120. totald := total - lastTotal
  121. idled := idleAll - lastIdleAll
  122. lastTotal = total
  123. lastIdleAll = idleAll
  124. if totald == 0 {
  125. return 0, nil
  126. }
  127. busy := totald - idled
  128. pct := float64(busy) / float64(totald) * 100.0
  129. if pct > 100 {
  130. pct = 100
  131. }
  132. return pct, nil
  133. }