sys_linux.go 3.2 KB

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