26
func TFTPHandler(next string) func(filename string, rf io.ReaderFrom) error {
27
return func(filename string, rf io.ReaderFrom) error {
28
log.Printf("tftp request: %s", filename)
30
file, err := ipxeFiles.Open(filepath.Join("data", filename))
37
contents, err := io.ReadAll(file)
42
var script bytes.Buffer
44
if err = scriptTemplate.Execute(&script, struct {
52
contents, err = patchScript(contents, script.Bytes())
54
return fmt.Errorf("error patching %q: %w", filename, err)
57
_, err = rf.ReadFrom(bytes.NewReader(contents))
64
var scriptTemplate = template.Must(template.New("iPXE embedded").Parse(`#!ipxe
65
prompt --key 0x02 --timeout 2000 Press Ctrl-B for the iPXE command line... && shell ||
67
{{/* print interfaces */}}
70
{{/* retry 10 times overall */}}
79
{{/* try DHCP on each interface */}}
80
isset ${net${idx}/mac} || goto exhausted
83
iflinkwait --timeout 5000 net${idx} || goto next_iface
84
dhcp net${idx} || goto next_iface
91
{{/* attempt boot, if fails try next iface */}}
94
chain --replace {{ .Next }} || goto next_iface
98
echo Failed to iPXE boot successfully via all interfaces
100
iseq ${x} ${attempts} && goto fail ||
110
echo Failed to get a valid response after ${attempts} attempts
113
echo Rebooting in 5 seconds...
119
placeholderStart = []byte("# *PLACEHOLDER START*")
120
placeholderEnd = []byte("# *PLACEHOLDER END*")
127
func patchScript(contents, script []byte) ([]byte, error) {
128
start := bytes.Index(contents, placeholderStart)
130
return nil, errors.New("placeholder start not found")
133
end := bytes.Index(contents, placeholderEnd)
135
return nil, errors.New("placeholder end not found")
139
return nil, errors.New("placeholder end before start")
142
end += len(placeholderEnd)
144
length := end - start
146
if len(script) > length {
147
return nil, fmt.Errorf("script size %d is larger than placeholder space %d", len(script), length)
150
script = append(script, bytes.Repeat([]byte{'\n'}, length-len(script))...)
152
copy(contents[start:end], script)