diff --git a/internal/pxe/supervisor.go b/internal/pxe/supervisor.go index 7e86f7d..0fe41df 100644 --- a/internal/pxe/supervisor.go +++ b/internal/pxe/supervisor.go @@ -3,6 +3,7 @@ package pxe import ( "context" "fmt" + "io" "log" "os" "os/exec" @@ -44,6 +45,7 @@ func (s *Supervisor) Start(ctx context.Context, hosts []model.Host) error { if err := os.MkdirAll(s.cfg.TFTPRoot, 0o755); err != nil { return fmt.Errorf("pxe: create tftp root: %w", err) } + s.seedIPXE() if err := s.writeConfig(hosts); err != nil { return err } @@ -133,6 +135,40 @@ func (s *Supervisor) startProcess(ctx context.Context) error { return nil } +var ipxeFiles = []struct{ src, dst string }{ + {"/usr/lib/ipxe/undionly.kpxe", "undionly.kpxe"}, + {"/usr/lib/ipxe/ipxe.efi", "ipxe.efi"}, +} + +func (s *Supervisor) seedIPXE() { + for _, f := range ipxeFiles { + dst := filepath.Join(s.cfg.TFTPRoot, f.dst) + if _, err := os.Stat(dst); err == nil { + continue + } + if err := copyFile(f.src, dst); err != nil { + log.Printf("pxe: copy %s → %s: %v", f.src, dst, err) + } else { + log.Printf("pxe: seeded %s", f.dst) + } + } +} + +func copyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + _, err = io.Copy(out, in) + return err +} + type dnsmasqConf struct { Interface string Subnet string @@ -147,7 +183,7 @@ port=0 interface={{.Interface}} bind-interfaces -dhcp-range=tag:known,{{.Subnet}},proxy +dhcp-range={{.Subnet}},proxy dhcp-hostsfile={{.HostsFile}} dhcp-ignore=tag:!known