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\d+)\* (?P[\w ]+)\t(HD\(.+,.+,(?P[0-9a-f-]+),.+,.+\))/File\((?P.+)\)`) var activeRegexp = regexp.MustCompile(`BootCurrent: (\d+)`) type EfiApp struct { ID int Name string Path string DiskID 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 } disk_id, ok := result["disk_id"] if !ok { return } return EfiApp{ ID: idInt, Name: strings.TrimSpace(name), Path: strings.TrimSpace(filepath), Active: idInt == activeId, DiskID: disk_id, }, 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 }