Show nice CPU usage
[godwmstat.git] / main.go
1 package main
2
3 import (
4         "bufio"
5         "flag"
6         "fmt"
7         "log"
8         "os"
9         "os/exec"
10         "strconv"
11         "strings"
12         "time"
13
14         "github.com/dustin/go-humanize"
15 )
16
17 var (
18         MyPID string = strconv.Itoa(os.Getpid())
19         CPU   string = "?"
20         Mem   string = "?"
21         ARC   string = "?"
22         Swap  string = "?"
23         Flags string = "?"
24         IOs   string = "?"
25         Net   string = "?"
26 )
27
28 func piper(c chan []string, name string, args ...string) error {
29         cmd := exec.Command(name, args...)
30         r, err := cmd.StdoutPipe()
31         if err != nil {
32                 return err
33         }
34         err = cmd.Start()
35         if err != nil {
36                 return err
37         }
38         scanner := bufio.NewScanner(r)
39         for scanner.Scan() {
40                 cols := strings.Fields(scanner.Text())
41                 if len(cols) > 0 {
42                         c <- cols
43                 }
44         }
45         if err = scanner.Err(); err != nil {
46                 cmd.Process.Kill()
47                 cmd.Wait()
48                 return err
49         }
50         return nil
51 }
52
53 func bg(cmd string, args ...string) chan []string {
54         c := make(chan []string)
55         go func() {
56                 for {
57                         err := piper(c, cmd, args...)
58                         if err != nil {
59                                 log.Println("error:", cmd, ":", err)
60                         }
61                         time.Sleep(time.Second)
62                 }
63         }()
64         return c
65 }
66
67 func top() {
68         for cols := range bg("top", "-b", "-d", "infinity", "-p", MyPID, "-s", "5", "infinity") {
69                 switch cols[0] {
70                 case "CPU:":
71                         CPU = strings.ReplaceAll(
72                                 fmt.Sprintf("%s sys:%s int:%s", cols[1], cols[5], cols[7]),
73                                 "%", "",
74                         )
75                 case "Mem:":
76                         Mem = strings.ReplaceAll(strings.Join(cols[1:], " "), ",", "")
77                 case "ARC:":
78                         ARC = cols[1]
79                 case "Swap:":
80                         if len(cols) >= 5 && cols[4] == "Used," {
81                                 Swap = cols[3] + " Swap "
82                         } else {
83                                 Swap = ""
84                         }
85                 }
86         }
87 }
88
89 func iostat() {
90         var stats []string
91         for cols := range bg("iostat", "-d", "-w", "1", "-x") {
92                 if cols[0] == "device" {
93                         IOs = strings.Join(stats, " ")
94                         stats = nil
95                         continue
96                 }
97                 if len(cols) < 4 || (cols[1] == "0" && cols[2] == "0") {
98                         continue
99                 }
100                 stats = append(stats, fmt.Sprintf("%s:%s/%s", cols[0], cols[1], cols[2]))
101         }
102 }
103
104 func netstat() {
105         for cols := range bg("netstat", "-I", "bridge0", "-n", "1") {
106                 if _, err := strconv.Atoi(cols[0]); err != nil {
107                         continue
108                 }
109                 ibps, err := strconv.Atoi(cols[3])
110                 if err != nil {
111                         continue
112                 }
113                 obps, err := strconv.Atoi(cols[6])
114                 if err != nil {
115                         continue
116                 }
117                 Net = fmt.Sprintf(
118                         "%s/%s %s / %s",
119                         cols[0], cols[4],
120                         humanize.IBytes(uint64(ibps)),
121                         humanize.IBytes(uint64(obps)),
122                 )
123         }
124 }
125
126 func flagfiles() {
127         ents, err := os.ReadDir("/tmp/stargrave-flags")
128         if err != nil {
129                 log.Println(err)
130                 return
131         }
132         fs := make([]string, 0, len(ents))
133         for _, ent := range ents {
134                 fs = append(fs, ent.Name())
135         }
136         Flags = strings.Join(fs, " ")
137 }
138
139 func main() {
140         xsetroot := flag.Bool("xsetroot", false, "Call xsetroot")
141         flag.Parse()
142         go func() {
143                 for {
144                         flagfiles()
145                         time.Sleep(20 * time.Second)
146                 }
147         }()
148         go top()
149         go iostat()
150         go netstat()
151         var now time.Time
152         var status string
153         var cmd *exec.Cmd
154         for {
155                 now = time.Now()
156                 status = fmt.Sprintf(
157                         "[%s]  [%s]  [%s]  [%s%s %s ARC]  [%s] %s",
158                         IOs, Net, CPU, Swap, Mem, ARC, Flags,
159                         now.Format("2006-01-02 15:04:05"),
160                 )
161                 if *xsetroot {
162                         cmd = exec.Command("xsetroot", "-name", status)
163                         cmd.Run()
164                 } else {
165                         fmt.Println(status)
166                 }
167                 time.Sleep(time.Second)
168         }
169 }