agi.sysinfo.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package agi
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "sync"
  7. "time"
  8. "github.com/robertkrimen/otto"
  9. "imuslab.com/arozos/mod/agi/static"
  10. "imuslab.com/arozos/mod/disk/diskspace"
  11. "imuslab.com/arozos/mod/info/logger"
  12. usageinfo "imuslab.com/arozos/mod/info/usageinfo"
  13. "imuslab.com/arozos/mod/network/netstat"
  14. )
  15. /*
  16. AGI System Info Library
  17. Author: tobychui
  18. Exposes CPU, RAM and network usage to AGI scripts via the "sysinfo" library.
  19. Usage in AGI:
  20. requirelib("sysinfo");
  21. var cpu = sysinfo.getCPUUsage(); // float percentage 0-100
  22. var ram = sysinfo.getRAMUsage(); // {used, total, percent}
  23. var net = sysinfo.getNetworkUsage(); // {rxRate, txRate, rxTotal, txTotal}
  24. */
  25. // networkSample holds a single point-in-time reading of cumulative network bytes.
  26. type networkSample struct {
  27. rxBytes int64
  28. txBytes int64
  29. timestamp time.Time
  30. }
  31. var (
  32. netMu sync.Mutex
  33. prevNetSample *networkSample
  34. )
  35. func (g *Gateway) SysinfoLibRegister() {
  36. err := g.RegisterLib("sysinfo", g.injectSysinfoLibFunctions)
  37. if err != nil {
  38. logger.PrintAndLog("Agi", fmt.Sprint(err), nil)
  39. os.Exit(1)
  40. }
  41. }
  42. func (g *Gateway) injectSysinfoLibFunctions(payload *static.AgiLibInjectionPayload) {
  43. vm := payload.VM
  44. // CPU Usage – returns a float64 percentage (0–100)
  45. vm.Set("_sysinfo_getcpu", func(call otto.FunctionCall) otto.Value {
  46. usage := usageinfo.GetCPUUsage()
  47. result, _ := vm.ToValue(usage)
  48. return result
  49. })
  50. // RAM Usage – returns JSON {used, total, percent}
  51. vm.Set("_sysinfo_getram", func(call otto.FunctionCall) otto.Value {
  52. used, total := usageinfo.GetNumericRAMUsage()
  53. percent := float64(0)
  54. if total > 0 {
  55. percent = float64(used) / float64(total) * 100.0
  56. }
  57. resp := map[string]interface{}{
  58. "used": used,
  59. "total": total,
  60. "percent": percent,
  61. }
  62. js, _ := json.Marshal(resp)
  63. result, _ := vm.ToValue(string(js))
  64. return result
  65. })
  66. // Network Usage – returns JSON {rxRate, txRate, rxTotal, txTotal} (bytes and bytes/sec)
  67. vm.Set("_sysinfo_getnet", func(call otto.FunctionCall) otto.Value {
  68. rxRate, txRate, rxTotal, txTotal := getNetworkUsage()
  69. resp := map[string]interface{}{
  70. "rxRate": rxRate,
  71. "txRate": txRate,
  72. "rxTotal": rxTotal,
  73. "txTotal": txTotal,
  74. }
  75. js, _ := json.Marshal(resp)
  76. result, _ := vm.ToValue(string(js))
  77. return result
  78. })
  79. // Disk Info – returns JSON array of logical disk volumes
  80. vm.Set("_sysinfo_getdisk", func(call otto.FunctionCall) otto.Value {
  81. disks := diskspace.GetAllLogicDiskInfo()
  82. js, _ := json.Marshal(disks)
  83. result, _ := vm.ToValue(string(js))
  84. return result
  85. })
  86. //nolint:errcheck
  87. vm.Run(`
  88. var sysinfo = {};
  89. sysinfo.getCPUUsage = function() {
  90. return _sysinfo_getcpu();
  91. };
  92. sysinfo.getRAMUsage = function() {
  93. var raw = _sysinfo_getram();
  94. try { return JSON.parse(raw); } catch(e) { return {used: -1, total: -1, percent: 0}; }
  95. };
  96. sysinfo.getNetworkUsage = function() {
  97. var raw = _sysinfo_getnet();
  98. try { return JSON.parse(raw); } catch(e) { return {rxRate: 0, txRate: 0, rxTotal: 0, txTotal: 0}; }
  99. };
  100. sysinfo.getDiskInfo = function() {
  101. var raw = _sysinfo_getdisk();
  102. try { return JSON.parse(raw); } catch(e) { return []; }
  103. };
  104. `)
  105. }
  106. // getNetworkUsage returns byte rates and totals by delegating to the shared
  107. // netstat.GetNetworkInterfaceStats helper (supports Linux, Darwin, Windows).
  108. // GetNetworkInterfaceStats returns accumulated bits, so we convert to bytes before
  109. // computing the per-second rate against the previous sample.
  110. func getNetworkUsage() (rxRate float64, txRate float64, rxTotal int64, txTotal int64) {
  111. rxBits, txBits, err := netstat.GetNetworkInterfaceStats()
  112. if err != nil {
  113. return 0, 0, 0, 0
  114. }
  115. // Convert accumulated bits → bytes
  116. rx := rxBits / 8
  117. tx := txBits / 8
  118. now := time.Now()
  119. netMu.Lock()
  120. defer netMu.Unlock()
  121. if prevNetSample != nil {
  122. elapsed := now.Sub(prevNetSample.timestamp).Seconds()
  123. if elapsed > 0 {
  124. rxRate = float64(rx-prevNetSample.rxBytes) / elapsed
  125. txRate = float64(tx-prevNetSample.txBytes) / elapsed
  126. if rxRate < 0 {
  127. rxRate = 0
  128. }
  129. if txRate < 0 {
  130. txRate = 0
  131. }
  132. }
  133. }
  134. prevNetSample = &networkSample{
  135. rxBytes: rx,
  136. txBytes: tx,
  137. timestamp: now,
  138. }
  139. return rxRate, txRate, rx, tx
  140. }