gokrazy/status.go
Michael Stapelberg 38af7fd18d Initial commit
2017-03-04 11:22:48 +01:00

157 lines
3.8 KiB
Go

package gokrazy
import (
"bytes"
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"github.com/gokrazy/gokrazy/internal/bundled"
"golang.org/x/sys/unix"
)
func parseMeminfo() map[string]int64 {
meminfo, err := ioutil.ReadFile("/proc/meminfo")
if err != nil {
return nil
}
vals := make(map[string]int64)
for _, line := range strings.Split(string(meminfo), "\n") {
if !strings.HasPrefix(line, "MemTotal") &&
!strings.HasPrefix(line, "MemAvailable") {
continue
}
parts := strings.Split(line, ":")
if len(parts) < 2 {
continue
}
val, err := strconv.ParseInt(strings.TrimSpace(strings.TrimSuffix(parts[1], " kB")), 0, 64)
if err != nil {
continue
}
vals[parts[0]] = val * 1024 // KiB to B
}
return vals
}
var commonTmpls = mustParseCommonTmpls()
func mustParseCommonTmpls() *template.Template {
t := template.New("root")
t = template.Must(t.New("header").Parse(bundled.Asset("header.tmpl")))
t = template.Must(t.New("footer").Parse(bundled.Asset("footer.tmpl")))
return t
}
var overviewTmpl = template.Must(template.Must(commonTmpls.Clone()).New("overview").
Funcs(map[string]interface{}{
"restarting": func(t time.Time) bool {
return time.Since(t).Seconds() < 5
},
"last": func(stdout, stderr []string) string {
if len(stdout) == 0 && len(stderr) == 0 {
return ""
}
both := append(stdout, stderr...)
sort.Strings(both)
return both[len(both)-1]
},
"megabytes": func(val int64) string {
return fmt.Sprintf("%.1f MiB", float64(val)/1024/1024)
},
"gigabytes": func(val int64) string {
return fmt.Sprintf("%.1f GiB", float64(val)/1024/1024/1024)
},
"baseName": func(path string) string {
return filepath.Base(path)
},
"initRss": func() int64 {
return rssOfPid(1)
},
"rssPercentage": func(meminfo map[string]int64, rss int64) string {
used := float64(meminfo["MemTotal"] - meminfo["MemAvailable"])
return fmt.Sprintf("%.f", float64(rss)/used*100)
},
}).
Parse(bundled.Asset("overview.tmpl")))
var statusTmpl = template.Must(template.Must(commonTmpls.Clone()).New("statusTmpl").Parse(bundled.Asset("status.tmpl")))
func initStatus(services []*service) {
http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
path := r.FormValue("path")
var svc *service
for _, s := range services {
if s.cmd.Path != path {
continue
}
svc = s
break
}
var buf bytes.Buffer
if err := statusTmpl.Execute(&buf, struct {
Service *service
BuildTimestamp string
Hostname string
}{
Service: svc,
BuildTimestamp: buildTimestamp,
Hostname: hostname,
}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
io.Copy(w, &buf)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var st unix.Statfs_t
if err := unix.Statfs("/perm", &st); err != nil {
log.Printf("could not stat /perm: %v", err)
}
hosts, err := PrivateInterfaceAddrs()
if err != nil {
log.Printf("could not get addrs: %v", err)
}
var buf bytes.Buffer
if err := overviewTmpl.Execute(&buf, struct {
Services []*service
PermUsed int64
PermAvail int64
PermTotal int64
Hosts []string
BuildTimestamp string
Meminfo map[string]int64
Hostname string
}{
Services: services,
PermUsed: int64(st.Bsize) * int64(st.Blocks-st.Bfree),
PermAvail: int64(st.Bsize) * int64(st.Bavail),
PermTotal: int64(st.Bsize) * int64(st.Blocks),
Hosts: hosts,
BuildTimestamp: buildTimestamp,
Meminfo: parseMeminfo(),
Hostname: hostname,
}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
io.Copy(w, &buf)
})
}