diff --git a/assets/header.tmpl b/assets/header.tmpl index 19ef97c..257f77d 100644 --- a/assets/header.tmpl +++ b/assets/header.tmpl @@ -47,9 +47,19 @@ table { - + + + + + + {{ if (ne .Model "") }} + + + + + {{ end }} + + diff --git a/internal/bundled/GENERATED_bundled.go b/internal/bundled/GENERATED_bundled.go index 90b5687..6ecee28 100644 --- a/internal/bundled/GENERATED_bundled.go +++ b/internal/bundled/GENERATED_bundled.go @@ -12,7 +12,7 @@ var assets = map[string][]byte{ "assets/bootstrap-table-1.11.0.min.js": assets_7, "assets/jquery-3.1.1.min.js": assets_8, } -var assets_0 = []byte("\n\n{{ .Hostname }} — gokrazy\n\n\n\n\n \n\n
\n") +var assets_0 = []byte("\n\n{{ .Hostname }} — gokrazy\n\n\n\n\n \n\n
\n") var assets_1 = []byte("\n
\n\n\n\n\n\n") var assets_2 = []byte("{{ template \"header\" . }}\n\n
\n
\n\n

services

\n\n\n\n\n\n\n\n{{ range $idx, $svc := .Services }}\n\n\n\n\n{{ end }}\n\n
pathlast log line
\n{{ $svc.Name }}\n{{ if restarting $svc.Started }}\nrestarting\n{{ end }}\n{{ if $svc.Stopped }}\nstopped\n{{ end }}\n\n{{ last $svc.Stdout.Lines $svc.Stderr.Lines }}\n
\n
\n
\n

memory

\n{{ megabytes (index .Meminfo \"MemTotal\") }} total, {{ megabytes (index .Meminfo \"MemAvailable\") }} available
\nresident set size (RSS) by service:\n
\n\n{{ with $rss := initRss }}\n
\n\ninit\n
\n{{ end }}\n\n{{ range $idx, $svc := .Services }}\n{{ with $rss := $svc.RSS }}\n
\n\n{{ baseName $svc.Name }}\n
\n{{ end }}\n{{ end }}\n
\n\nunaccounted\n
\n
\n
\n\n
\n\n\n

storage

\n\n{{ if eq .PermAvail 0 }}\nNo permanent storage mounted. To create a filesystem for permanent storage, plug the SD card into a Linux computer and, if your SD card is /dev/sdb, use mkfs.ext4 /dev/sdb4.\n{{ else }}\n{{ .PermDev }}: {{ gigabytes .PermTotal }} total, {{ gigabytes .PermUsed }} used, {{ gigabytes .PermAvail }} avail
\n{{ end }}\n\n

private network addresses

\n
    \n{{ range $idx, $addr := .PrivateAddrs }}\n
  • {{ $addr }}
  • \n{{ end }}\n
\n\n

public network addresses

\n
    \n{{ range $idx, $addr := .PublicAddrs }}\n
  • {{ $addr }}
  • \n{{ end }}\n
\n\n\n
\n
\n\n{{ template \"footer\" . }}\n") var assets_3 = []byte("{{ template \"header\" . }}\n\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n
NameStartedActions
{{ .Service.Name }}{{ .Service.Started }}\n
\n \n \n \n
\n
\n \n \n \n
\n\n

stdout

\n
\n  {{ range $idx, $line := .Service.Stdout.Lines -}}\n    {{ $line }}\n  {{ end }}\n  
\n\n

stderr

\n
\n  {{ range $idx, $line := .Service.Stderr.Lines -}}\n    {{ $line }}\n  {{ end }}\n  
\n
\n
\n\n{{ template \"footer\" . }}\n") diff --git a/status.go b/status.go index 2751029..5c77c25 100644 --- a/status.go +++ b/status.go @@ -93,7 +93,38 @@ var overviewTmpl = template.Must(template.Must(commonTmpls.Clone()).New("overvie var statusTmpl = template.Must(template.Must(commonTmpls.Clone()).New("statusTmpl").Parse(bundled.Asset("status.tmpl"))) +// mustReadFile0 returns the file contents or an empty string if the file could +// not be read. All trailing \0 bytes are stripped (as found in +// /proc/device-tree/model). +func mustReadFile0(filename string) string { + b, err := ioutil.ReadFile(filename) + if err != nil { + return "" + } + if idx := bytes.IndexByte(b, 0); idx > -1 { + b = b[:idx] + } + return string(b) +} + +func model() string { + // the supported Raspberry Pis have this file + model := mustReadFile0("/proc/device-tree/model") + if model == "" { + // The PC Engines apu2c4 (and other PCs) have this file instead: + vendor := mustReadFile0("/sys/class/dmi/id/board_vendor") + name := mustReadFile0("/sys/class/dmi/id/board_name") + if vendor == "" || name == "" { + return "" // unsupported platform + } + model = strings.TrimSpace(vendor) + " " + strings.TrimSpace(name) + } + return strings.TrimSpace(model) +} + func initStatus(services []*service) { + model := model() + for _, fn := range []string{ "favicon.ico", "bootstrap-3.3.7.min.css", @@ -132,11 +163,13 @@ func initStatus(services []*service) { Service *service BuildTimestamp string Hostname string + Model string XsrfToken int32 }{ Service: svc, BuildTimestamp: buildTimestamp, Hostname: hostname, + Model: model, XsrfToken: token, }); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -172,6 +205,7 @@ func initStatus(services []*service) { BuildTimestamp string Meminfo map[string]int64 Hostname string + Model string }{ Services: services, PermDev: rootdev.Partition(rootdev.Perm), @@ -183,6 +217,7 @@ func initStatus(services []*service) { BuildTimestamp: buildTimestamp, Meminfo: parseMeminfo(), Hostname: hostname, + Model: model, }); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return