104 lines
2.6 KiB
Go
104 lines
2.6 KiB
Go
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
|
|
}
|