hostcache.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package hardwareinfo
  2. import (
  3. "net/http"
  4. "net/http/httptest"
  5. "sync"
  6. "time"
  7. )
  8. // refreshInterval controls how often drive, NIC and USB data is refreshed.
  9. // CPU model and total RAM are sampled once and never expire.
  10. const refreshInterval = 60 * time.Second
  11. type hostInfoCache struct {
  12. mu sync.RWMutex
  13. // Static: primed once, never refreshed.
  14. cpuBytes []byte
  15. ramBytes []byte
  16. // Volatile: refreshed every refreshInterval.
  17. driveBytes []byte
  18. ifcBytes []byte
  19. usbBytes []byte
  20. }
  21. var hic = &hostInfoCache{}
  22. // StartHostInfoCache primes all hardware caches in the background and
  23. // launches a ticker to keep volatile data (drives, NICs, USB) fresh.
  24. // Call this once at startup.
  25. func StartHostInfoCache() {
  26. go hic.refresh(true) // static + volatile initial prime
  27. go func() {
  28. ticker := time.NewTicker(refreshInterval)
  29. defer ticker.Stop()
  30. for range ticker.C {
  31. hic.refresh(false) // volatile-only periodic refresh
  32. }
  33. }()
  34. }
  35. // refresh collects data by invoking the existing HTTP handlers through an
  36. // in-process httptest recorder. When withStatic is true, CPU and RAM are
  37. // re-sampled; otherwise only drives, NICs and USB are refreshed.
  38. func (c *hostInfoCache) refresh(withStatic bool) {
  39. req, _ := http.NewRequest("GET", "/", nil)
  40. if withStatic {
  41. r1 := httptest.NewRecorder()
  42. GetCPUInfo(r1, req)
  43. r2 := httptest.NewRecorder()
  44. GetRamInfo(r2, req)
  45. c.mu.Lock()
  46. c.cpuBytes = r1.Body.Bytes()
  47. c.ramBytes = r2.Body.Bytes()
  48. c.mu.Unlock()
  49. }
  50. r3 := httptest.NewRecorder()
  51. GetDriveStat(r3, req)
  52. r4 := httptest.NewRecorder()
  53. Ifconfig(r4, req)
  54. r5 := httptest.NewRecorder()
  55. GetUSB(r5, req)
  56. c.mu.Lock()
  57. c.driveBytes = r3.Body.Bytes()
  58. c.ifcBytes = r4.Body.Bytes()
  59. c.usbBytes = r5.Body.Bytes()
  60. c.mu.Unlock()
  61. }
  62. // CachedGetCPUInfo serves CPU info from cache, falling back to a live call
  63. // for the brief window before the first background sample completes.
  64. func CachedGetCPUInfo(w http.ResponseWriter, r *http.Request) {
  65. hic.mu.RLock()
  66. b := hic.cpuBytes
  67. hic.mu.RUnlock()
  68. if len(b) == 0 {
  69. GetCPUInfo(w, r)
  70. return
  71. }
  72. w.Write(b)
  73. }
  74. // CachedGetRamInfo serves total RAM from cache.
  75. func CachedGetRamInfo(w http.ResponseWriter, r *http.Request) {
  76. hic.mu.RLock()
  77. b := hic.ramBytes
  78. hic.mu.RUnlock()
  79. if len(b) == 0 {
  80. GetRamInfo(w, r)
  81. return
  82. }
  83. w.Write(b)
  84. }
  85. // CachedGetDriveStat serves drive statistics from cache.
  86. func CachedGetDriveStat(w http.ResponseWriter, r *http.Request) {
  87. hic.mu.RLock()
  88. b := hic.driveBytes
  89. hic.mu.RUnlock()
  90. if len(b) == 0 {
  91. GetDriveStat(w, r)
  92. return
  93. }
  94. w.Write(b)
  95. }
  96. // CachedIfconfig serves NIC info from cache.
  97. func CachedIfconfig(w http.ResponseWriter, r *http.Request) {
  98. hic.mu.RLock()
  99. b := hic.ifcBytes
  100. hic.mu.RUnlock()
  101. if len(b) == 0 {
  102. Ifconfig(w, r)
  103. return
  104. }
  105. w.Write(b)
  106. }
  107. // CachedGetUSB serves USB device info from cache.
  108. func CachedGetUSB(w http.ResponseWriter, r *http.Request) {
  109. hic.mu.RLock()
  110. b := hic.usbBytes
  111. hic.mu.RUnlock()
  112. if len(b) == 0 {
  113. GetUSB(w, r)
  114. return
  115. }
  116. w.Write(b)
  117. }