sbctl/bundles.go

130 lines
3.1 KiB
Go

package sbctl
import (
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
)
type Bundle struct {
Output string `json:"output"`
IntelMicrocode string `json:"intel_microcode"`
AMDMicrocode string `json:"amd_microcode"`
KernelImage string `json:"kernel_image"`
Initramfs string `json:"initramfs"`
Cmdline string `json:"cmdline"`
Splash string `json:"splash"`
OSRelease string `json:"os_release"`
EFIStub string `json:"efi_stub"`
ESP string `json:"esp"`
}
type Bundles map[string]*Bundle
var BundleDBPath = filepath.Join(DatabasePath, "bundles.db")
func ReadBundleDatabase(dbpath string) (Bundles, error) {
f, err := ReadOrCreateFile(dbpath)
if err != nil {
return nil, err
}
bundles := make(Bundles)
json.Unmarshal(f, &bundles)
return bundles, nil
}
func WriteBundleDatabase(dbpath string, bundles Bundles) error {
data, err := json.MarshalIndent(bundles, "", " ")
if err != nil {
return err
}
err = os.WriteFile(dbpath, data, 0644)
if err != nil {
return err
}
return nil
}
func BundleIter(fn func(s *Bundle) error) error {
files, err := ReadBundleDatabase(BundleDBPath)
if err != nil {
return err
}
for _, s := range files {
if err := fn(s); err != nil {
return err
}
}
return nil
}
func GetEfistub() string {
candidates := []string{
"/lib/systemd/boot/efi/linuxx64.efi.stub",
"/lib/gummiboot/linuxx64.efi.stub",
}
for _, f := range candidates {
if _, err := os.Stat(f); err == nil {
return f
}
}
return ""
}
func NewBundle() (bundle *Bundle, err error) {
esp, err := GetESP()
if err != nil {
return
}
stub := GetEfistub()
if stub == "" {
panic("No EFISTUB file found. Please install systemd-boot or gummiboot!")
}
bundle = &Bundle{
Output: "",
IntelMicrocode: "",
AMDMicrocode: "",
KernelImage: "/boot/vmlinuz-linux",
Initramfs: "/boot/initramfs-linux.img",
Cmdline: "/etc/kernel/cmdline",
Splash: "",
OSRelease: "/usr/lib/os-release",
EFIStub: stub,
ESP: esp,
}
return
}
func GenerateBundle(bundle *Bundle) (bool, error) {
args := []string{
"--add-section", fmt.Sprintf(".osrel=%s", bundle.OSRelease), "--change-section-vma", ".osrel=0x20000",
"--add-section", fmt.Sprintf(".cmdline=%s", bundle.Cmdline), "--change-section-vma", ".cmdline=0x30000",
"--add-section", fmt.Sprintf(".linux=%s", bundle.KernelImage), "--change-section-vma", ".linux=0x2000000",
"--add-section", fmt.Sprintf(".initrd=%s", bundle.Initramfs), "--change-section-vma", ".initrd=0x3000000",
}
if bundle.Splash != "" {
args = append(args, "--add-section", fmt.Sprintf(".splash=%s", bundle.Splash), "--change-section-vma", ".splash=0x40000")
}
args = append(args, bundle.EFIStub, bundle.Output)
cmd := exec.Command("objcopy", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
if errors.Is(err, exec.ErrNotFound) {
return false, err
}
if exitError, ok := err.(*exec.ExitError); ok {
return exitError.ExitCode() == 0, nil
}
}
return true, nil
}