http-boot-config/config/prober/prober.go

102 lines
2 KiB
Go

package prober
import (
"bytes"
"errors"
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"git.faercol.me/faercol/http-boot-config/config/logger"
)
var efiBootmgrRegexp = regexp.MustCompile(`Boot(?P<id>\d+)\* (?P<name>[\w ]+)\t(\w+\(.*\))/File\((?P<filepath>.+)\)`)
var activeRegexp = regexp.MustCompile(`BootCurrent: (\d+)`)
type EfiApp struct {
ID int
Name string
Path string
Active bool
}
func efiAppFromBootMgrOutput(rawVal string, activeId int) (app EfiApp, ok bool) {
match := efiBootmgrRegexp.FindStringSubmatch(rawVal)
if len(match) == 0 {
return
}
result := make(map[string]string)
for i, name := range efiBootmgrRegexp.SubexpNames() {
if i != 0 && name != "" {
result[name] = match[i]
}
}
id, ok := result["id"]
if !ok {
return
}
idInt, err := strconv.Atoi(id)
if err != nil {
return app, false
}
name, ok := result["name"]
if !ok {
return
}
filepath, ok := result["filepath"]
if !ok {
return
}
return EfiApp{
ID: idInt,
Name: strings.TrimSpace(name),
Path: strings.TrimSpace(filepath),
Active: idInt == activeId,
}, true
}
func getActiveEFIApp(output string) (int, error) {
match := activeRegexp.FindStringSubmatch(output)
if len(match) == 0 {
return -1, errors.New("no active boot image found")
}
strId := match[1]
id, err := strconv.Atoi(strId)
if err != nil {
return -1, err
}
return id, nil
}
func GetEFIApps(log *logger.SimpleLogger) (apps []EfiApp, err error) {
cmd := exec.Command("/usr/bin/efibootmgr")
var out bytes.Buffer
cmd.Stdout = &out
err = cmd.Run()
if err != nil {
return
}
if cmd.ProcessState.ExitCode() != 0 {
return nil, fmt.Errorf("error running efibootmgr, returncode %d", cmd.ProcessState.ExitCode())
}
outStr := out.String()
activeBootID, err := getActiveEFIApp(outStr)
if err != nil {
return
}
for _, l := range strings.Split(outStr, "\n") {
log.Debugf("Parsing line %q", l)
app, ok := efiAppFromBootMgrOutput(l, activeBootID)
if !ok {
log.Debug("Line is not a valid EFI application")
continue
}
apps = append(apps, app)
}
return
}