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