host.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package stats
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "fmt"
  7. "io/ioutil"
  8. "net"
  9. "os"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. )
  14. var (
  15. delim = []byte{' '}
  16. )
  17. // Host represents host status.
  18. type Host struct {
  19. Sysname string
  20. Storages []*Storage
  21. Interfaces []*Interface
  22. }
  23. // MemStats represents the memory statistics.
  24. type MemStats struct {
  25. Total int64 // total memory in byte
  26. PageSize int64 // a page size in byte
  27. KernelPages int64
  28. UserPages Gauge
  29. SwapPages Gauge
  30. Malloced Gauge // kernel malloced data in byte
  31. Graphics Gauge // kernel graphics data in byte
  32. }
  33. // Gauge is used/available gauge.
  34. type Gauge struct {
  35. Used int64
  36. Avail int64
  37. }
  38. func (g Gauge) Free() int64 {
  39. return g.Avail - g.Used
  40. }
  41. // ReadMemStats reads memory statistics from /dev/swap.
  42. func ReadMemStats(ctx context.Context, opts ...Option) (*MemStats, error) {
  43. cfg := newConfig(opts...)
  44. swap := filepath.Join(cfg.rootdir, "/dev/swap")
  45. f, err := os.Open(swap)
  46. if err != nil {
  47. return nil, err
  48. }
  49. defer f.Close()
  50. var stat MemStats
  51. m := map[string]interface{}{
  52. "memory": &stat.Total,
  53. "pagesize": &stat.PageSize,
  54. "kernel": &stat.KernelPages,
  55. "user": &stat.UserPages,
  56. "swap": &stat.SwapPages,
  57. "kernel malloc": &stat.Malloced,
  58. "kernel draw": &stat.Graphics,
  59. }
  60. scanner := bufio.NewScanner(f)
  61. for scanner.Scan() {
  62. fields := bytes.SplitN(scanner.Bytes(), delim, 2)
  63. if len(fields) < 2 {
  64. continue
  65. }
  66. switch key := string(fields[1]); key {
  67. case "memory", "pagesize", "kernel":
  68. v := m[key].(*int64)
  69. n, err := strconv.ParseInt(string(fields[0]), 10, 64)
  70. if err != nil {
  71. return nil, err
  72. }
  73. *v = n
  74. case "user", "swap", "kernel malloc", "kernel draw":
  75. v := m[key].(*Gauge)
  76. if err := parseGauge(string(fields[0]), v); err != nil {
  77. return nil, err
  78. }
  79. }
  80. }
  81. if err := scanner.Err(); err != nil {
  82. return nil, err
  83. }
  84. return &stat, nil
  85. }
  86. func parseGauge(s string, r *Gauge) error {
  87. a := strings.SplitN(s, "/", 2)
  88. if len(a) != 2 {
  89. return fmt.Errorf("can't parse ratio: %s", s)
  90. }
  91. var p intParser
  92. u := p.ParseInt64(a[0], 10)
  93. n := p.ParseInt64(a[1], 10)
  94. if err := p.Err(); err != nil {
  95. return err
  96. }
  97. r.Used = u
  98. r.Avail = n
  99. return nil
  100. }
  101. type Interface struct {
  102. Name string
  103. Addr string
  104. }
  105. const (
  106. numEther = 8 // see ether(3)
  107. numIpifc = 16 // see ip(3)
  108. )
  109. // ReadInterfaces reads network interfaces from etherN.
  110. func ReadInterfaces(ctx context.Context, opts ...Option) ([]*Interface, error) {
  111. cfg := newConfig(opts...)
  112. var a []*Interface
  113. for i := 0; i < numEther; i++ {
  114. p, err := readInterface(cfg.rootdir, i)
  115. if os.IsNotExist(err) {
  116. continue
  117. }
  118. if err != nil {
  119. return nil, err
  120. }
  121. a = append(a, p)
  122. }
  123. return a, nil
  124. }
  125. func readInterface(netroot string, i int) (*Interface, error) {
  126. ether := fmt.Sprintf("ether%d", i)
  127. dir := filepath.Join(netroot, ether)
  128. info, err := os.Stat(dir)
  129. if err != nil {
  130. return nil, err
  131. }
  132. if !info.IsDir() {
  133. return nil, fmt.Errorf("%s: is not directory", dir)
  134. }
  135. addr, err := ioutil.ReadFile(filepath.Join(dir, "addr"))
  136. if err != nil {
  137. return nil, err
  138. }
  139. return &Interface{
  140. Name: ether,
  141. Addr: string(addr),
  142. }, nil
  143. }
  144. var (
  145. netdirs = []string{"/net", "/net.alt"}
  146. )
  147. // ReadHost reads host status.
  148. func ReadHost(ctx context.Context, opts ...Option) (*Host, error) {
  149. cfg := newConfig(opts...)
  150. var h Host
  151. name, err := readSysname(cfg.rootdir)
  152. if err != nil {
  153. return nil, err
  154. }
  155. h.Sysname = name
  156. a, err := ReadStorages(ctx, opts...)
  157. if err != nil {
  158. return nil, err
  159. }
  160. h.Storages = a
  161. for _, s := range netdirs {
  162. netroot := filepath.Join(cfg.rootdir, s)
  163. ifaces, err := ReadInterfaces(ctx, WithRootDir(netroot))
  164. if err != nil {
  165. return nil, err
  166. }
  167. h.Interfaces = append(h.Interfaces, ifaces...)
  168. }
  169. return &h, nil
  170. }
  171. func readSysname(rootdir string) (string, error) {
  172. file := filepath.Join(rootdir, "/dev/sysname")
  173. b, err := ioutil.ReadFile(file)
  174. if err != nil {
  175. return "", err
  176. }
  177. return string(bytes.TrimSpace(b)), nil
  178. }
  179. type IPStats struct {
  180. ID int // number of interface in ipifc dir
  181. Device string // associated physical device
  182. MTU int // max transfer unit
  183. Sendra6 uint8 // on == send router adv
  184. Recvra6 uint8 // on == recv router adv
  185. Pktin int64 // packets read
  186. Pktout int64 // packets written
  187. Errin int64 // read errors
  188. Errout int64 // write errors
  189. }
  190. type Iplifc struct {
  191. IP net.IP
  192. Mask net.IPMask
  193. Net net.IP // ip & mask
  194. PerfLifetime int64 // preferred lifetime
  195. ValidLifetime int64 // valid lifetime
  196. }
  197. type Ipv6rp struct {
  198. // TODO(lufia): see ip(2)
  199. }