http-boot-config/config/efivar/efivar.go

105 lines
2.6 KiB
Go
Raw Normal View History

2023-08-22 20:39:23 +00:00
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
}