SEBRAUC/src/sysinfo/sysinfo.go
Theta-Dev 0125f4e3fe
All checks were successful
continuous-integration/drone/push Build is passing
fix rauc status command
2021-12-06 01:32:03 +01:00

181 lines
4.1 KiB
Go

package sysinfo
import (
"encoding/json"
"os"
"regexp"
"strconv"
"strings"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
)
type SystemInfo struct {
Hostname string `json:"hostname"`
OsName string `json:"os_name"`
OsVersion string `json:"os_version"`
Uptime int `json:"uptime"`
RaucCompatible string `json:"rauc_compatible"`
RaucVariant string `json:"rauc_variant"`
RaucRootfs map[string]Rootfs `json:"rauc_rootfs"`
}
type Rootfs struct {
Device string `json:"device"`
Type string `json:"type"`
bootname string
Mountpoint *string `json:"mountpoint"`
Bootable bool `json:"bootable"`
Booted bool `json:"booted"`
Primary bool `json:"primary"`
}
type raucInfo struct {
Compatible string `json:"compatible"`
Variant string `json:"variant"`
Booted string `json:"booted"`
BootPrimary string `json:"boot_primary"`
Slots []map[string]raucFS `json:"slots"`
}
type raucFS struct {
Class string `json:"class"`
Device string `json:"device"`
Type string `json:"type"`
Bootname string `json:"bootname"`
State string `json:"state"`
Mountpoint *string `json:"mountpoint"`
BootStatus string `json:"boot_status"`
}
type osRelease struct {
OsName string `json:"os_name"`
OsVersion string `json:"os_version"`
}
var (
rexpOsName = regexp.MustCompile(`(?m)^NAME="(.+)"`)
rexpOsVersion = regexp.MustCompile(`(?m)^VERSION="(.+)"`)
rexpUptime = regexp.MustCompile(`^\d+`)
)
func parseRaucInfo(raucInfoJson []byte) (raucInfo, error) {
res := raucInfo{}
err := json.Unmarshal(raucInfoJson, &res)
return res, err
}
func parseOsRelease(osReleaseFile string) (osRelease, error) {
osReleaseTxt, err := os.ReadFile(osReleaseFile)
if err != nil {
return osRelease{}, err
}
nameMatch := rexpOsName.FindSubmatch(osReleaseTxt)
versionMatch := rexpOsVersion.FindSubmatch(osReleaseTxt)
name := ""
if nameMatch != nil {
name = string(nameMatch[1])
}
version := ""
if versionMatch != nil {
version = string(versionMatch[1])
}
return osRelease{
OsName: name,
OsVersion: version,
}, nil
}
func mapRootfs(rinf raucInfo) map[string]Rootfs {
res := make(map[string]Rootfs)
for _, slot := range rinf.Slots {
for name, fs := range slot {
if fs.Class == "rootfs" {
res[name] = Rootfs{
Device: fs.Device,
Type: fs.Type,
bootname: fs.Bootname,
Mountpoint: fs.Mountpoint,
Bootable: fs.BootStatus == "good",
Booted: fs.State == "booted",
Primary: rinf.BootPrimary == name,
}
}
}
}
return res
}
func getFSNameFromBootname(rfslist map[string]Rootfs, bootname string) string {
for name, rfs := range rfslist {
if rfs.bootname == bootname {
return name
}
}
return "n/a"
}
func mapSysinfo(rinf raucInfo, osr osRelease, uptime int, hostname string) SystemInfo {
rfslist := mapRootfs(rinf)
return SystemInfo{
Hostname: hostname,
OsName: osr.OsName,
OsVersion: osr.OsVersion,
Uptime: uptime,
RaucCompatible: rinf.Compatible,
RaucVariant: rinf.Variant,
RaucRootfs: rfslist,
}
}
func getUptime() (int, error) {
uptimeRaw, err := os.ReadFile("/proc/uptime")
if err != nil {
return 0, err
}
uptimeChars := rexpUptime.Find(uptimeRaw)
return strconv.Atoi(string(uptimeChars))
}
func getHostname() string {
hostname, err := os.ReadFile("/etc/hostname")
if err != nil {
return ""
}
return strings.TrimSpace(string(hostname))
}
func GetSysinfo() (SystemInfo, error) {
cmd := util.CommandFromString(util.RaucCmd + " status --output-format=json")
rinfJson, err := cmd.Output()
if err != nil {
return SystemInfo{}, err
}
rinf, err := parseRaucInfo(rinfJson)
if err != nil {
return SystemInfo{}, err
}
osinf, err := parseOsRelease("/etc/os-release")
if err != nil {
return SystemInfo{}, err
}
uptime, err := getUptime()
if err != nil {
return SystemInfo{}, err
}
hostname := getHostname()
return mapSysinfo(rinf, osinf, uptime, hostname), nil
}