diff options
Diffstat (limited to 'fsbm.go')
| -rw-r--r-- | fsbm.go | 145 |
1 files changed, 145 insertions, 0 deletions
@@ -0,0 +1,145 @@ +package main + +import "fmt" +import "bufio" +import "flag" +import "time" +import "os" +import "strings" +import "strconv" + +type Iface struct { + Name string + RxBytes uint64 + RxPackets uint64 + RxErrs uint64 + TxBytes uint64 + TxPackets uint64 + TxErrs uint64 +} + +// human readable bits 'n bytes per second +// assumes bytes input. + +func humanBps(bps float64, raw bool, bits bool) string { + prefixes := []string{"", "k", "M", "G", "T", "P", "E", "Z", "Y"} + unit := "B/s" + + if bits { + bps *= 8 + unit = "b/s" + } + + if raw { + return fmt.Sprintf("%.2f", bps) + } + + prefix := 0 + for prefix+1 < len(prefixes) && bps >= 1000 { + bps /= 1000 + prefix++ + } + + return fmt.Sprintf("%.2f %s%s", bps, prefixes[prefix], unit) +} + +func main() { + bitFlag := flag.Bool("b", false, "render bits not bytes") + intervalFlag := flag.String("d", "1s", "update interval (e.g. 500ms, 1s, 2s)") + pathFlag := flag.String("f", "/proc/net/dev", "path") + interfaceFlag := flag.String("i", "", "interface to report on") + rawFlag := flag.Bool("r", false, "show raw values, not human") + versionFlag := flag.Bool("V", false, "show version information") + flag.Parse() + + if *versionFlag { + fmt.Println("fsbm-go version 1.0") + os.Exit(0) + } + + interval, err := time.ParseDuration(*intervalFlag) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid duration (%s): %v", *intervalFlag, err) + os.Exit(1) + } + + if _, err := os.Stat(*pathFlag); err != nil { + fmt.Fprintf(os.Stderr, "cannot access %s: %v\n", *pathFlag, err) + os.Exit(1) + } + + limitIfaces := false + requestedIfaces := make(map[string]struct{}) + if *interfaceFlag != "" { + for _, name := range strings.Split(*interfaceFlag, ",") { + requestedIfaces[name] = struct{}{} + } + if len(requestedIfaces) > 0 { + limitIfaces = true + } + } + + + ifaces := make(map[string]Iface) + var prevTime time.Time + + for { + + openTime := time.Now() + file, err := os.Open(*pathFlag) // For read access. + if err != nil { + fmt.Fprintf(os.Stderr, "cannot access %s: %v\n", *pathFlag, err) + os.Exit(1) + } + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + + line := strings.TrimSpace(scanner.Text()) + + name, rest, found := strings.Cut(line, ":") + if !found { + continue + } + if limitIfaces { + _, ok := requestedIfaces[name]; + if !ok { + continue + } + } + + fields := strings.Fields(rest) + if len(fields) != 16 { + fmt.Fprintf(os.Stderr, "cannot parse %s", line) + os.Exit(1) + } + + cur := Iface{ + Name: name, + } + cur.RxBytes, _ = strconv.ParseUint(fields[0], 10, 64) // RxBytes uint64 + cur.RxPackets, _ = strconv.ParseUint(fields[1], 10, 64) // RxPackets uint64 + cur.RxErrs, _ = strconv.ParseUint(fields[2], 10, 64) // RxErrs uint64 + cur.TxBytes, _ = strconv.ParseUint(fields[8], 10, 64) // TxBytes uint64 + cur.TxPackets, _ = strconv.ParseUint(fields[9], 10, 64) // TxPackets uint64 + cur.TxErrs, _ = strconv.ParseUint(fields[10], 10, 64) // TxErrs uint64 + + prev, exists := ifaces[name] + if exists { + // deltas + timeDiff := openTime.Sub(prevTime).Seconds() + rxRate := float64(cur.RxBytes-prev.RxBytes) / timeDiff + txRate := float64(cur.TxBytes-prev.TxBytes) / timeDiff + fmt.Printf("%s: (%s, %s) ", name, humanBps(rxRate, *rawFlag, *bitFlag), humanBps(txRate, *rawFlag, *bitFlag)) + } + + ifaces[name] = cur + + } + + fmt.Printf("\n"); + file.Close() + prevTime = openTime + time.Sleep(interval) + } +} |
