| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- package stats
- import (
- "bufio"
- "bytes"
- "context"
- "fmt"
- "io/ioutil"
- "net"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- )
- var (
- delim = []byte{' '}
- )
- // Host represents host status.
- type Host struct {
- Sysname string
- Storages []*Storage
- Interfaces []*Interface
- }
- // MemStats represents the memory statistics.
- type MemStats struct {
- Total int64 // total memory in byte
- PageSize int64 // a page size in byte
- KernelPages int64
- UserPages Gauge
- SwapPages Gauge
- Malloced Gauge // kernel malloced data in byte
- Graphics Gauge // kernel graphics data in byte
- }
- // Gauge is used/available gauge.
- type Gauge struct {
- Used int64
- Avail int64
- }
- func (g Gauge) Free() int64 {
- return g.Avail - g.Used
- }
- // ReadMemStats reads memory statistics from /dev/swap.
- func ReadMemStats(ctx context.Context, opts ...Option) (*MemStats, error) {
- cfg := newConfig(opts...)
- swap := filepath.Join(cfg.rootdir, "/dev/swap")
- f, err := os.Open(swap)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- var stat MemStats
- m := map[string]interface{}{
- "memory": &stat.Total,
- "pagesize": &stat.PageSize,
- "kernel": &stat.KernelPages,
- "user": &stat.UserPages,
- "swap": &stat.SwapPages,
- "kernel malloc": &stat.Malloced,
- "kernel draw": &stat.Graphics,
- }
- scanner := bufio.NewScanner(f)
- for scanner.Scan() {
- fields := bytes.SplitN(scanner.Bytes(), delim, 2)
- if len(fields) < 2 {
- continue
- }
- switch key := string(fields[1]); key {
- case "memory", "pagesize", "kernel":
- v := m[key].(*int64)
- n, err := strconv.ParseInt(string(fields[0]), 10, 64)
- if err != nil {
- return nil, err
- }
- *v = n
- case "user", "swap", "kernel malloc", "kernel draw":
- v := m[key].(*Gauge)
- if err := parseGauge(string(fields[0]), v); err != nil {
- return nil, err
- }
- }
- }
- if err := scanner.Err(); err != nil {
- return nil, err
- }
- return &stat, nil
- }
- func parseGauge(s string, r *Gauge) error {
- a := strings.SplitN(s, "/", 2)
- if len(a) != 2 {
- return fmt.Errorf("can't parse ratio: %s", s)
- }
- var p intParser
- u := p.ParseInt64(a[0], 10)
- n := p.ParseInt64(a[1], 10)
- if err := p.Err(); err != nil {
- return err
- }
- r.Used = u
- r.Avail = n
- return nil
- }
- type Storage struct {
- Name string
- Model string
- Capacity int64
- }
- type Interface struct {
- Name string
- Addr string
- }
- const (
- numEther = 8 // see ether(3)
- numIpifc = 16 // see ip(3)
- )
- // ReadInterfaces reads network interfaces from etherN.
- func ReadInterfaces(ctx context.Context, opts ...Option) ([]*Interface, error) {
- cfg := newConfig(opts...)
- var a []*Interface
- for i := 0; i < numEther; i++ {
- p, err := readInterface(cfg.rootdir, i)
- if os.IsNotExist(err) {
- continue
- }
- if err != nil {
- return nil, err
- }
- a = append(a, p)
- }
- return a, nil
- }
- func readInterface(netroot string, i int) (*Interface, error) {
- ether := fmt.Sprintf("ether%d", i)
- dir := filepath.Join(netroot, ether)
- info, err := os.Stat(dir)
- if err != nil {
- return nil, err
- }
- if !info.IsDir() {
- return nil, fmt.Errorf("%s: is not directory", dir)
- }
- addr, err := ioutil.ReadFile(filepath.Join(dir, "addr"))
- if err != nil {
- return nil, err
- }
- return &Interface{
- Name: ether,
- Addr: string(addr),
- }, nil
- }
- var (
- netdirs = []string{"/net", "/net.alt"}
- )
- // ReadHost reads host status.
- func ReadHost(ctx context.Context, opts ...Option) (*Host, error) {
- cfg := newConfig(opts...)
- var h Host
- name, err := readSysname(cfg.rootdir)
- if err != nil {
- return nil, err
- }
- h.Sysname = name
- a, err := readStorages(cfg.rootdir)
- if err != nil {
- return nil, err
- }
- h.Storages = a
- for _, s := range netdirs {
- netroot := filepath.Join(cfg.rootdir, s)
- ifaces, err := ReadInterfaces(ctx, WithRootDir(netroot))
- if err != nil {
- return nil, err
- }
- h.Interfaces = append(h.Interfaces, ifaces...)
- }
- return &h, nil
- }
- func readSysname(rootdir string) (string, error) {
- file := filepath.Join(rootdir, "/dev/sysname")
- b, err := ioutil.ReadFile(file)
- if err != nil {
- return "", err
- }
- return string(bytes.TrimSpace(b)), nil
- }
- func readStorages(rootdir string) ([]*Storage, error) {
- sdctl := filepath.Join(rootdir, "/dev/sdctl")
- f, err := os.Open(sdctl)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- var a []*Storage
- scanner := bufio.NewScanner(f)
- for scanner.Scan() {
- fields := bytes.Split(scanner.Bytes(), delim)
- if len(fields) == 0 {
- continue
- }
- exp := string(fields[0]) + "*"
- if !strings.HasPrefix(exp, "sd") {
- continue
- }
- dir := filepath.Join(rootdir, "/dev", exp)
- m, err := filepath.Glob(dir)
- if err != nil {
- return nil, err
- }
- for _, dir := range m {
- s, err := readStorage(dir)
- if err != nil {
- return nil, err
- }
- a = append(a, s)
- }
- }
- if err := scanner.Err(); err != nil {
- return nil, err
- }
- return a, nil
- }
- func readStorage(dir string) (*Storage, error) {
- ctl := filepath.Join(dir, "ctl")
- f, err := os.Open(ctl)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- var s Storage
- s.Name = filepath.Base(dir)
- scanner := bufio.NewScanner(f)
- for scanner.Scan() {
- line := scanner.Bytes()
- switch {
- case bytes.HasPrefix(line, []byte("inquiry")):
- s.Model = string(bytes.TrimSpace(line[7:]))
- case bytes.HasPrefix(line, []byte("geometry")):
- fields := bytes.Split(line, delim)
- if len(fields) < 3 {
- continue
- }
- var p intParser
- sec := p.ParseInt64(string(fields[1]), 10)
- size := p.ParseInt64(string(fields[2]), 10)
- if err := p.Err(); err != nil {
- return nil, err
- }
- s.Capacity = sec * size
- }
- }
- if err := scanner.Err(); err != nil {
- return nil, err
- }
- return &s, nil
- }
- type IPStats struct {
- ID int // number of interface in ipifc dir
- Device string // associated physical device
- MTU int // max transfer unit
- Sendra6 uint8 // on == send router adv
- Recvra6 uint8 // on == recv router adv
- Pktin int64 // packets read
- Pktout int64 // packets written
- Errin int64 // read errors
- Errout int64 // write errors
- }
- type Iplifc struct {
- IP net.IP
- Mask net.IPMask
- Net net.IP // ip & mask
- PerfLifetime int64 // preferred lifetime
- ValidLifetime int64 // valid lifetime
- }
- type Ipv6rp struct {
- // TODO(lufia): see ip(2)
- }
|