summaryrefslogtreecommitdiff
path: root/vendor/github.com/lufia/plan9stats
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-05-11 21:12:57 -0600
committermo khan <mo@mokhan.ca>2025-05-11 21:12:57 -0600
commit60440f90dca28e99a31dd328c5f6d5dc0f9b6a2e (patch)
tree2f54adf55086516f162f0a55a5347e6b25f7f176 /vendor/github.com/lufia/plan9stats
parent05ca9b8d3a9c7203a3a3b590beaa400900bd9007 (diff)
chore: vendor go dependencies
Diffstat (limited to 'vendor/github.com/lufia/plan9stats')
-rw-r--r--vendor/github.com/lufia/plan9stats/.gitignore12
-rw-r--r--vendor/github.com/lufia/plan9stats/LICENSE29
-rw-r--r--vendor/github.com/lufia/plan9stats/README.md2
-rw-r--r--vendor/github.com/lufia/plan9stats/cpu.go288
-rw-r--r--vendor/github.com/lufia/plan9stats/doc.go2
-rw-r--r--vendor/github.com/lufia/plan9stats/host.go303
-rw-r--r--vendor/github.com/lufia/plan9stats/int.go31
-rw-r--r--vendor/github.com/lufia/plan9stats/opts.go21
-rw-r--r--vendor/github.com/lufia/plan9stats/stats.go88
9 files changed, 776 insertions, 0 deletions
diff --git a/vendor/github.com/lufia/plan9stats/.gitignore b/vendor/github.com/lufia/plan9stats/.gitignore
new file mode 100644
index 0000000..f1c181e
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/.gitignore
@@ -0,0 +1,12 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
diff --git a/vendor/github.com/lufia/plan9stats/LICENSE b/vendor/github.com/lufia/plan9stats/LICENSE
new file mode 100644
index 0000000..a6d47e8
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2019, KADOTA, Kyohei
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/lufia/plan9stats/README.md b/vendor/github.com/lufia/plan9stats/README.md
new file mode 100644
index 0000000..a21700c
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/README.md
@@ -0,0 +1,2 @@
+# plan9stats
+A module for retrieving statistics of Plan 9
diff --git a/vendor/github.com/lufia/plan9stats/cpu.go b/vendor/github.com/lufia/plan9stats/cpu.go
new file mode 100644
index 0000000..a101b91
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/cpu.go
@@ -0,0 +1,288 @@
+package stats
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// CPUType represents /dev/cputype.
+type CPUType struct {
+ Name string
+ Clock int // clock rate in MHz
+}
+
+func ReadCPUType(ctx context.Context, opts ...Option) (*CPUType, error) {
+ cfg := newConfig(opts...)
+ var c CPUType
+ if err := readCPUType(cfg.rootdir, &c); err != nil {
+ return nil, err
+ }
+ return &c, nil
+}
+
+type SysStats struct {
+ ID int
+ NumCtxSwitch int64
+ NumInterrupt int64
+ NumSyscall int64
+ NumFault int64
+ NumTLBFault int64
+ NumTLBPurge int64
+ LoadAvg int64 // in units of milli-CPUs and is decayed over time
+ Idle int // percentage
+ Interrupt int // percentage
+}
+
+// ReadSysStats reads system statistics from /dev/sysstat.
+func ReadSysStats(ctx context.Context, opts ...Option) ([]*SysStats, error) {
+ cfg := newConfig(opts...)
+ file := filepath.Join(cfg.rootdir, "/dev/sysstat")
+ f, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ scanner := bufio.NewScanner(f)
+ var stats []*SysStats
+ for scanner.Scan() {
+ a := strings.Fields(scanner.Text())
+ if len(a) != 10 {
+ continue
+ }
+ var (
+ p intParser
+ stat SysStats
+ )
+ stat.ID = p.ParseInt(a[0], 10)
+ stat.NumCtxSwitch = p.ParseInt64(a[1], 10)
+ stat.NumInterrupt = p.ParseInt64(a[2], 10)
+ stat.NumSyscall = p.ParseInt64(a[3], 10)
+ stat.NumFault = p.ParseInt64(a[4], 10)
+ stat.NumTLBFault = p.ParseInt64(a[5], 10)
+ stat.NumTLBPurge = p.ParseInt64(a[6], 10)
+ stat.LoadAvg = p.ParseInt64(a[7], 10)
+ stat.Idle = p.ParseInt(a[8], 10)
+ stat.Interrupt = p.ParseInt(a[9], 10)
+ if err := p.Err(); err != nil {
+ return nil, err
+ }
+ stats = append(stats, &stat)
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+ return stats, nil
+}
+
+func readCPUType(rootdir string, c *CPUType) error {
+ file := filepath.Join(rootdir, "/dev/cputype")
+ b, err := ioutil.ReadFile(file)
+ if err != nil {
+ return err
+ }
+ b = bytes.TrimSpace(b)
+ i := bytes.LastIndexByte(b, ' ')
+ if i < 0 {
+ return fmt.Errorf("%s: invalid format", file)
+ }
+ clock, err := strconv.Atoi(string(b[i+1:]))
+ if err != nil {
+ return err
+ }
+ c.Name = string(b[:i])
+ c.Clock = clock
+ return nil
+}
+
+// Time represents /dev/time.
+type Time struct {
+ Unix time.Duration
+ UnixNano time.Duration
+ Ticks int64 // clock ticks
+ Freq int64 //cloc frequency
+}
+
+// Uptime returns uptime.
+func (t *Time) Uptime() time.Duration {
+ v := float64(t.Ticks) / float64(t.Freq)
+ return time.Duration(v*1000_000_000) * time.Nanosecond
+}
+
+func ReadTime(ctx context.Context, opts ...Option) (*Time, error) {
+ cfg := newConfig(opts...)
+ file := filepath.Join(cfg.rootdir, "/dev/time")
+ var t Time
+ if err := readTime(file, &t); err != nil {
+ return nil, err
+ }
+ return &t, nil
+}
+
+// ProcStatus represents a /proc/n/status.
+type ProcStatus struct {
+ Name string
+ User string
+ State string
+ Times CPUTime
+ MemUsed int64 // in units of 1024 bytes
+ BasePriority uint32 // 0(low) to 19(high)
+ Priority uint32 // 0(low) to 19(high)
+}
+
+// CPUTime represents /dev/cputime or a part of /proc/n/status.
+type CPUTime struct {
+ User time.Duration // the time in user mode (millisecconds)
+ Sys time.Duration
+ Real time.Duration
+ ChildUser time.Duration // exited children and descendants time in user mode
+ ChildSys time.Duration
+ ChildReal time.Duration
+}
+
+// CPUStats emulates Linux's /proc/stat.
+type CPUStats struct {
+ User time.Duration
+ Sys time.Duration
+ Idle time.Duration
+}
+
+func ReadCPUStats(ctx context.Context, opts ...Option) (*CPUStats, error) {
+ cfg := newConfig(opts...)
+ a, err := ReadSysStats(ctx, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ dir := filepath.Join(cfg.rootdir, "/proc")
+ d, err := os.Open(dir)
+ if err != nil {
+ return nil, err
+ }
+ defer d.Close()
+
+ names, err := d.Readdirnames(0)
+ if err != nil {
+ return nil, err
+ }
+ var up uint32parser
+ pids := make([]uint32, len(names))
+ for i, s := range names {
+ pids[i] = up.Parse(s)
+ }
+ if up.err != nil {
+ return nil, err
+ }
+ sort.Slice(pids, func(i, j int) bool {
+ return pids[i] < pids[j]
+ })
+
+ var stat CPUStats
+ for _, pid := range pids {
+ s := strconv.FormatUint(uint64(pid), 10)
+ file := filepath.Join(dir, s, "status")
+ var p ProcStatus
+ if err := readProcStatus(file, &p); err != nil {
+ return nil, err
+ }
+ stat.User += p.Times.User
+ stat.Sys += p.Times.Sys
+ }
+
+ var t Time
+ file := filepath.Join(cfg.rootdir, "/dev/time")
+ if err := readTime(file, &t); err != nil {
+ return nil, err
+ }
+ // In multi-processor host, Idle should multiple by number of cores.
+ u := t.Uptime() * time.Duration(len(a))
+ stat.Idle = u - stat.User - stat.Sys
+ return &stat, nil
+}
+
+func readProcStatus(file string, p *ProcStatus) error {
+ b, err := ioutil.ReadFile(file)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return err
+ }
+ fields := strings.Fields(string(b))
+ if len(fields) != 12 {
+ return errors.New("invalid format")
+ }
+ p.Name = string(fields[0])
+ p.User = string(fields[1])
+ p.State = string(fields[2])
+ var up uint32parser
+ p.Times.User = time.Duration(up.Parse(fields[3])) * time.Millisecond
+ p.Times.Sys = time.Duration(up.Parse(fields[4])) * time.Millisecond
+ p.Times.Real = time.Duration(up.Parse(fields[5])) * time.Millisecond
+ p.Times.ChildUser = time.Duration(up.Parse(fields[6])) * time.Millisecond
+ p.Times.ChildSys = time.Duration(up.Parse(fields[7])) * time.Millisecond
+ p.Times.ChildReal = time.Duration(up.Parse(fields[8])) * time.Millisecond
+ p.MemUsed, err = strconv.ParseInt(fields[9], 10, 64)
+ if err != nil {
+ return err
+ }
+ p.BasePriority = up.Parse(fields[10])
+ p.Priority = up.Parse(fields[11])
+ return up.err
+}
+
+func readTime(file string, t *Time) error {
+ b, err := ioutil.ReadFile(file)
+ if err != nil {
+ return err
+ }
+ fields := strings.Fields(string(b))
+ if len(fields) != 4 {
+ return errors.New("invalid format")
+ }
+ n, err := strconv.ParseInt(fields[0], 10, 32)
+ if err != nil {
+ return err
+ }
+ t.Unix = time.Duration(n) * time.Second
+ v, err := strconv.ParseInt(fields[1], 10, 64)
+ if err != nil {
+ return err
+ }
+ t.UnixNano = time.Duration(v) * time.Nanosecond
+ t.Ticks, err = strconv.ParseInt(fields[2], 10, 64)
+ if err != nil {
+ return err
+ }
+ t.Freq, err = strconv.ParseInt(fields[3], 10, 64)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+type uint32parser struct {
+ err error
+}
+
+func (p *uint32parser) Parse(s string) uint32 {
+ if p.err != nil {
+ return 0
+ }
+ n, err := strconv.ParseUint(s, 10, 32)
+ if err != nil {
+ p.err = err
+ return 0
+ }
+ return uint32(n)
+}
diff --git a/vendor/github.com/lufia/plan9stats/doc.go b/vendor/github.com/lufia/plan9stats/doc.go
new file mode 100644
index 0000000..10e398e
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/doc.go
@@ -0,0 +1,2 @@
+// Package stats provides statistic utilities for Plan 9.
+package stats
diff --git a/vendor/github.com/lufia/plan9stats/host.go b/vendor/github.com/lufia/plan9stats/host.go
new file mode 100644
index 0000000..957e903
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/host.go
@@ -0,0 +1,303 @@
+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)
+}
diff --git a/vendor/github.com/lufia/plan9stats/int.go b/vendor/github.com/lufia/plan9stats/int.go
new file mode 100644
index 0000000..db133c4
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/int.go
@@ -0,0 +1,31 @@
+package stats
+
+import (
+ "strconv"
+)
+
+type intParser struct {
+ err error
+}
+
+func (p *intParser) ParseInt(s string, base int) int {
+ if p.err != nil {
+ return 0
+ }
+ var n int64
+ n, p.err = strconv.ParseInt(s, base, 0)
+ return int(n)
+}
+
+func (p *intParser) ParseInt64(s string, base int) int64 {
+ if p.err != nil {
+ return 0
+ }
+ var n int64
+ n, p.err = strconv.ParseInt(s, base, 64)
+ return n
+}
+
+func (p *intParser) Err() error {
+ return p.err
+}
diff --git a/vendor/github.com/lufia/plan9stats/opts.go b/vendor/github.com/lufia/plan9stats/opts.go
new file mode 100644
index 0000000..05b7d03
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/opts.go
@@ -0,0 +1,21 @@
+package stats
+
+type Config struct {
+ rootdir string
+}
+
+type Option func(*Config)
+
+func newConfig(opts ...Option) *Config {
+ var cfg Config
+ for _, opt := range opts {
+ opt(&cfg)
+ }
+ return &cfg
+}
+
+func WithRootDir(dir string) Option {
+ return func(cfg *Config) {
+ cfg.rootdir = dir
+ }
+}
diff --git a/vendor/github.com/lufia/plan9stats/stats.go b/vendor/github.com/lufia/plan9stats/stats.go
new file mode 100644
index 0000000..d4ecdcf
--- /dev/null
+++ b/vendor/github.com/lufia/plan9stats/stats.go
@@ -0,0 +1,88 @@
+package stats
+
+import (
+ "bufio"
+ "context"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+type InterfaceStats struct {
+ PacketsReceived int64 // in packets
+ Link int // link status
+ PacketsSent int64 // out packets
+ NumCRCErr int // input CRC errors
+ NumOverflows int // packet overflows
+ NumSoftOverflows int // software overflow
+ NumFramingErr int // framing errors
+ NumBufferingErr int // buffering errors
+ NumOutputErr int // output errors
+ Promiscuous int // number of promiscuous opens
+ Mbps int // megabits per sec
+ Addr string
+}
+
+func ReadInterfaceStats(ctx context.Context, opts ...Option) (*InterfaceStats, error) {
+ cfg := newConfig(opts...)
+ file := filepath.Join(cfg.rootdir, "stats")
+ f, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ var stats InterfaceStats
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ s := strings.TrimSpace(scanner.Text())
+ a := strings.SplitN(s, ":", 2)
+ if len(a) != 2 {
+ continue
+ }
+ var p intParser
+ v := strings.TrimSpace(a[1])
+ switch a[0] {
+ case "in":
+ stats.PacketsReceived = p.ParseInt64(v, 10)
+ case "link":
+ stats.Link = p.ParseInt(v, 10)
+ case "out":
+ stats.PacketsSent = p.ParseInt64(v, 10)
+ case "crc":
+ stats.NumCRCErr = p.ParseInt(v, 10)
+ case "overflows":
+ stats.NumOverflows = p.ParseInt(v, 10)
+ case "soft overflows":
+ stats.NumSoftOverflows = p.ParseInt(v, 10)
+ case "framing errs":
+ stats.NumFramingErr = p.ParseInt(v, 10)
+ case "buffer errs":
+ stats.NumBufferingErr = p.ParseInt(v, 10)
+ case "output errs":
+ stats.NumOutputErr = p.ParseInt(v, 10)
+ case "prom":
+ stats.Promiscuous = p.ParseInt(v, 10)
+ case "mbps":
+ stats.Mbps = p.ParseInt(v, 10)
+ case "addr":
+ stats.Addr = v
+ }
+ if err := p.Err(); err != nil {
+ return nil, err
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+ return &stats, nil
+}
+
+type TCPStats struct {
+ MaxConn int
+ MaxSegment int
+ ActiveOpens int
+ PassiveOpens int
+ EstablishedResets int
+ CurrentEstablished int
+}