raidcheck/mdstat.go
2021-12-21 20:51:12 +01:00

83 lines
2.3 KiB
Go

package main
import (
"os"
"regexp"
"strconv"
"strings"
"time"
)
var (
firstLineRegex = regexp.MustCompile(`^(.*?)\s:\s(\w+)?\sraid(\d{1,2})\s((?:[\w\d]+\[\d+\](\s|$))+)`)
secondLineRegex = regexp.MustCompile(`^\s{6}\d+\sblocks\s(?:super\s\d\.\d\s)?(?:level\s\d,\s)?(?:\d+\w\s\w+, )?(?:\w+\s\d\s)?\[(\d+)\/(\d+)\]\s\[([U_]+)\]`)
actionLineRegex = regexp.MustCompile(`^\s{6}\[[=>.]{21}\]\s\s(\w+?)\s=\s(\d+?\.\d+?)%\s\(\d+?\/\d+?\)\sfinish=(\d+?\.\d+?)(\w+?)\sspeed=(\d+?)([BKMG])\/sec`)
deviceRegex = regexp.MustCompile(`^([\w\d]+)\[(\d+)\]$`)
)
func mdstat() <-chan RaidState {
ch := make(chan RaidState, 4)
go func(out chan<- RaidState) {
defer close(ch)
for {
data, err := os.ReadFile("/proc/mdstat")
if err != nil {
panic(err)
}
lines := strings.Split(string(data), "\n")
state := RaidState{}
for _, line := range lines {
if match := firstLineRegex.FindStringSubmatch(line); match != nil {
state.Name = match[1]
state.State = match[2]
state.Level = match[3]
devices := strings.Split(match[4], " ")
state.Devices = make([]string, len(devices))
for _, device := range devices {
if match := deviceRegex.FindStringSubmatch(device); match != nil {
deviceIndex, _ := strconv.Atoi(match[2])
state.Devices[deviceIndex] = match[1]
}
}
} else if match := secondLineRegex.FindStringSubmatch(line); match != nil {
state.RegisteredDeviceCount, _ = strconv.Atoi(match[1])
state.UsedDeviceCount, _ = strconv.Atoi(match[2])
state.DevicesUp = map[string]bool{}
for i, rn := range match[3] {
state.DevicesUp[state.Devices[i]] = rn == 'U'
}
} else if match := actionLineRegex.FindStringSubmatch(line); match != nil {
state.Action = new(RaidAction)
state.Action.Name = match[1]
state.Action.Progress, _ = strconv.ParseFloat(match[2], 64)
state.Action.Duration = parseFinished(match[3], match[4])
state.Action.DurationFormatted = state.Action.Duration.String()
state.Action.Finished = time.Now().Add(state.Action.Duration)
}
}
out <- state
time.Sleep(time.Minute)
}
}(ch)
return ch
}
func parseFinished(value, unit string) time.Duration {
switch unit {
case "sec":
unit = "s"
case "min":
unit = "m"
}
dur, _ := time.ParseDuration(value + unit)
return dur
}