package efivar import ( "bytes" "errors" "fmt" "io/ioutil" "os" "os/exec" "strconv" "strings" "github.com/google/uuid" ) // const efiVarDir = "/sys/firmware/efi/efivars" const execPath = "/usr/bin/efivar" var VendorID = uuid.MustParse("638a24ce-a0ce-4908-8152-b2a4e8b2f29d") const ( VarBootUUID = "HTTP_BOOT_UUID" VarBootPort = "HTTP_BOOT_MCAST_PORT" VarBootGroup = "HTTP_BOOT_MCAST_GROUP" VarBootIP = "HTTP_BOOT_IP" ) var ErrNotFound = errors.New("variable not found") func toFullVarName(gid uuid.UUID, varName string) string { return strings.Join([]string{gid.String(), varName}, "-") } func numSliceToBytes(in []string) ([]byte, error) { res := []byte{} for i, n := range in { strippedN := strings.TrimSpace(n) if len(strippedN) == 0 { continue } intN, err := strconv.Atoi(strippedN) if err != nil { return nil, fmt.Errorf("invalid number %s at index %d", n, i) } if intN < 0 || intN > 255 { return nil, fmt.Errorf("value %d at index %d out of range", intN, i) } res = append(res, byte(intN)) } return res, nil } func GetVar(gid uuid.UUID, varName string) ([]byte, error) { cmd := exec.Command(execPath, "-n", toFullVarName(gid, varName), "-d") var outBuf, errBuf bytes.Buffer cmd.Stdout = &outBuf cmd.Stderr = &errBuf err := cmd.Run() var exitErr exec.ExitError e := &exitErr if err != nil && !errors.As(err, &e) { return nil, err } if cmd.ProcessState.ExitCode() != 0 { stderr := errBuf.String() if strings.Contains(stderr, "No such file or directory") { return nil, ErrNotFound } return nil, fmt.Errorf("error getting variable, returncode %d, stderr %q", cmd.ProcessState.ExitCode(), stderr) } nums := strings.Split(outBuf.String(), " ") return numSliceToBytes(nums) } func SetVar(gid uuid.UUID, varName string, value []byte) error { tmpVarFile, err := os.CreateTemp("/tmp", "efitmpvar") if err != nil { return fmt.Errorf("failed to create tmp file to write variable: %w", err) } defer tmpVarFile.Close() defer os.Remove(tmpVarFile.Name()) if err := ioutil.WriteFile(tmpVarFile.Name(), value, 0o500); err != nil { return fmt.Errorf("failed to write variable value to tmp file: %w", err) } cmd := exec.Command(execPath, "-n", toFullVarName(gid, varName), "-w", "-f", tmpVarFile.Name()) var errBuf bytes.Buffer cmd.Stderr = &errBuf err = cmd.Run() var exitErr exec.ExitError e := &exitErr if err != nil && !errors.As(err, &e) { return err } if cmd.ProcessState.ExitCode() != 0 { stderr := errBuf.String() return fmt.Errorf("error setting variable, returncode %d, stderr %q", cmd.ProcessState.ExitCode(), stderr) } return nil }