host.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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 Storage struct {
  102. Name string
  103. Model string
  104. Capacity int64
  105. }
  106. type Interface struct {
  107. Name string
  108. Addr string
  109. }
  110. const (
  111. numEther = 8 // see ether(3)
  112. numIpifc = 16 // see ip(3)
  113. )
  114. // ReadInterfaces reads network interfaces from etherN.
  115. func ReadInterfaces(ctx context.Context, opts ...Option) ([]*Interface, error) {
  116. cfg := newConfig(opts...)
  117. var a []*Interface
  118. for i := 0; i < numEther; i++ {
  119. p, err := readInterface(cfg.rootdir, i)
  120. if os.IsNotExist(err) {
  121. continue
  122. }
  123. if err != nil {
  124. return nil, err
  125. }
  126. a = append(a, p)
  127. }
  128. return a, nil
  129. }
  130. func readInterface(netroot string, i int) (*Interface, error) {
  131. ether := fmt.Sprintf("ether%d", i)
  132. dir := filepath.Join(netroot, ether)
  133. info, err := os.Stat(dir)
  134. if err != nil {
  135. return nil, err
  136. }
  137. if !info.IsDir() {
  138. return nil, fmt.Errorf("%s: is not directory", dir)
  139. }
  140. addr, err := ioutil.ReadFile(filepath.Join(dir, "addr"))
  141. if err != nil {
  142. return nil, err
  143. }
  144. return &Interface{
  145. Name: ether,
  146. Addr: string(addr),
  147. }, nil
  148. }
  149. var (
  150. netdirs = []string{"/net", "/net.alt"}
  151. )
  152. // ReadHost reads host status.
  153. func ReadHost(ctx context.Context, opts ...Option) (*Host, error) {
  154. cfg := newConfig(opts...)
  155. var h Host
  156. name, err := readSysname(cfg.rootdir)
  157. if err != nil {
  158. return nil, err
  159. }
  160. h.Sysname = name
  161. a, err := readStorages(cfg.rootdir)
  162. if err != nil {
  163. return nil, err
  164. }
  165. h.Storages = a
  166. for _, s := range netdirs {
  167. netroot := filepath.Join(cfg.rootdir, s)
  168. ifaces, err := ReadInterfaces(ctx, WithRootDir(netroot))
  169. if err != nil {
  170. return nil, err
  171. }
  172. h.Interfaces = append(h.Interfaces, ifaces...)
  173. }
  174. return &h, nil
  175. }
  176. func readSysname(rootdir string) (string, error) {
  177. file := filepath.Join(rootdir, "/dev/sysname")
  178. b, err := ioutil.ReadFile(file)
  179. if err != nil {
  180. return "", err
  181. }
  182. return string(bytes.TrimSpace(b)), nil
  183. }
  184. func readStorages(rootdir string) ([]*Storage, error) {
  185. sdctl := filepath.Join(rootdir, "/dev/sdctl")
  186. f, err := os.Open(sdctl)
  187. if err != nil {
  188. return nil, err
  189. }
  190. defer f.Close()
  191. var a []*Storage
  192. scanner := bufio.NewScanner(f)
  193. for scanner.Scan() {
  194. fields := bytes.Split(scanner.Bytes(), delim)
  195. if len(fields) == 0 {
  196. continue
  197. }
  198. exp := string(fields[0]) + "*"
  199. if !strings.HasPrefix(exp, "sd") {
  200. continue
  201. }
  202. dir := filepath.Join(rootdir, "/dev", exp)
  203. m, err := filepath.Glob(dir)
  204. if err != nil {
  205. return nil, err
  206. }
  207. for _, dir := range m {
  208. s, err := readStorage(dir)
  209. if err != nil {
  210. return nil, err
  211. }
  212. a = append(a, s)
  213. }
  214. }
  215. if err := scanner.Err(); err != nil {
  216. return nil, err
  217. }
  218. return a, nil
  219. }
  220. func readStorage(dir string) (*Storage, error) {
  221. ctl := filepath.Join(dir, "ctl")
  222. f, err := os.Open(ctl)
  223. if err != nil {
  224. return nil, err
  225. }
  226. defer f.Close()
  227. var s Storage
  228. s.Name = filepath.Base(dir)
  229. scanner := bufio.NewScanner(f)
  230. for scanner.Scan() {
  231. line := scanner.Bytes()
  232. switch {
  233. case bytes.HasPrefix(line, []byte("inquiry")):
  234. s.Model = string(bytes.TrimSpace(line[7:]))
  235. case bytes.HasPrefix(line, []byte("geometry")):
  236. fields := bytes.Split(line, delim)
  237. if len(fields) < 3 {
  238. continue
  239. }
  240. var p intParser
  241. sec := p.ParseInt64(string(fields[1]), 10)
  242. size := p.ParseInt64(string(fields[2]), 10)
  243. if err := p.Err(); err != nil {
  244. return nil, err
  245. }
  246. s.Capacity = sec * size
  247. }
  248. }
  249. if err := scanner.Err(); err != nil {
  250. return nil, err
  251. }
  252. return &s, nil
  253. }
  254. type IPStats struct {
  255. ID int // number of interface in ipifc dir
  256. Device string // associated physical device
  257. MTU int // max transfer unit
  258. Sendra6 uint8 // on == send router adv
  259. Recvra6 uint8 // on == recv router adv
  260. Pktin int64 // packets read
  261. Pktout int64 // packets written
  262. Errin int64 // read errors
  263. Errout int64 // write errors
  264. }
  265. type Iplifc struct {
  266. IP net.IP
  267. Mask net.IPMask
  268. Net net.IP // ip & mask
  269. PerfLifetime int64 // preferred lifetime
  270. ValidLifetime int64 // valid lifetime
  271. }
  272. type Ipv6rp struct {
  273. // TODO(lufia): see ip(2)
  274. }