github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/netboot/ipxe/ipxe.go (about) 1 // Copyright 2017-2019 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package ipxe implements a trivial IPXE config file parser. 6 package ipxe 7 8 import ( 9 "errors" 10 "fmt" 11 "io" 12 "log" 13 "net/url" 14 "strings" 15 16 "github.com/u-root/u-root/pkg/boot" 17 "github.com/u-root/u-root/pkg/uio" 18 "github.com/u-root/u-root/pkg/urlfetch" 19 ) 20 21 var ( 22 // ErrNotIpxeScript is returned when the config file is not an 23 // ipxe script. 24 ErrNotIpxeScript = errors.New("config file is not ipxe as it does not start with #!ipxe") 25 ) 26 27 // parser encapsulates a parsed ipxe configuration file. 28 // 29 // We currently only support kernel and initrd commands. 30 type parser struct { 31 bootImage *boot.LinuxImage 32 33 schemes urlfetch.Schemes 34 } 35 36 // ParseConfig returns a new configuration with the file at URL and default 37 // schemes. 38 // 39 // See ParseConfigWithSchemes for more details. 40 func ParseConfig(configURL *url.URL) (*boot.LinuxImage, error) { 41 return ParseConfigWithSchemes(configURL, urlfetch.DefaultSchemes) 42 } 43 44 // ParseConfigWithSchemes returns a new configuration with the file at URL 45 // and schemes `s`. 46 // 47 // `s` is used to get files referred to by URLs in the configuration. 48 func ParseConfigWithSchemes(configURL *url.URL, s urlfetch.Schemes) (*boot.LinuxImage, error) { 49 c := &parser{ 50 schemes: s, 51 } 52 if err := c.getAndParseFile(configURL); err != nil { 53 return nil, err 54 } 55 return c.bootImage, nil 56 } 57 58 // getAndParse parses the config file downloaded from `url` and fills in `c`. 59 func (c *parser) getAndParseFile(u *url.URL) error { 60 r, err := c.schemes.LazyFetch(u) 61 if err != nil { 62 return err 63 } 64 data, err := uio.ReadAll(r) 65 if err != nil { 66 return err 67 } 68 config := string(data) 69 if !strings.HasPrefix(config, "#!ipxe") { 70 return ErrNotIpxeScript 71 } 72 log.Printf("Got ipxe config file %s:\n%s\n", r, config) 73 return c.parseIpxe(config) 74 } 75 76 // getFile parses `surl` and returns an io.Reader for the requested url. 77 func (c *parser) getFile(surl string) (io.ReaderAt, error) { 78 u, err := url.Parse(surl) 79 if err != nil { 80 return nil, fmt.Errorf("could not parse URL %q: %v", surl, err) 81 } 82 return c.schemes.LazyFetch(u) 83 } 84 85 // parseIpxe parses `config` and constructs a BootImage for `c`. 86 func (c *parser) parseIpxe(config string) error { 87 // A trivial ipxe script parser. 88 // Currently only supports kernel and initrd commands. 89 c.bootImage = &boot.LinuxImage{} 90 91 for _, line := range strings.Split(config, "\n") { 92 // Skip blank lines and comment lines. 93 line = strings.TrimSpace(line) 94 if line == "" || line[0] == '#' { 95 continue 96 } 97 98 args := strings.Fields(line) 99 if len(args) <= 1 { 100 log.Printf("Ignoring unsupported ipxe cmd: %s", line) 101 continue 102 } 103 cmd := strings.ToLower(args[0]) 104 105 switch cmd { 106 case "kernel": 107 k, err := c.getFile(args[1]) 108 if err != nil { 109 return err 110 } 111 c.bootImage.Kernel = k 112 113 // Add cmdline if there are any. 114 if len(args) > 2 { 115 c.bootImage.Cmdline = strings.Join(args[2:], " ") 116 } 117 118 case "initrd": 119 i, err := c.getFile(args[1]) 120 if err != nil { 121 return err 122 } 123 c.bootImage.Initrd = i 124 125 default: 126 log.Printf("Ignoring unsupported ipxe cmd: %s", line) 127 } 128 } 129 130 return nil 131 }