github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/systembooter/netbooter.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 systembooter 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "log" 11 "os" 12 "os/exec" 13 "strconv" 14 ) 15 16 // NetBooter implements the Booter interface for booting over DHCPv6. 17 // See NewNetBooterDHCPv6 for details on the fields. 18 type NetBooter struct { 19 Type string `json:"type"` 20 Method string `json:"method"` 21 MAC string `json:"mac"` 22 OverrideURL *string `json:"override_url,omitempty"` 23 Retries *int `json:"retries,omitempty"` 24 DebugOnFailure bool `json:"debug_on_failure,omitempty"` 25 } 26 27 // NewNetBooter parses a boot entry config and returns a Booter instance, or an 28 // error if any 29 func NewNetBooter(config []byte) (Booter, error) { 30 // The configuration format for a NetBooterDHCPv6 entry is a JSON with the 31 // following structure: 32 // { 33 // "type": "netboot", 34 // "method": "<method>", 35 // "mac": "<mac_addr>", 36 // "override_url": "<url>", 37 // "retries": <num_retries>, 38 // "debug_on_failure": <true|false> 39 // } 40 // 41 // `type` is always set to "netboot". 42 // `method` is one of "dhcpv6", "slaac" or "dhcpv4". 43 // `mac` is the MAC address of the interface to use to boot. 44 // `override_url` is an optional URL used to override the boot file URL used 45 // to fetch the network boot program. This field becomes mandatory if 46 // `method` is set to "slaac". 47 // `retries` is the number of times a DHCP request should be retried if 48 // failed. If unspecified, it will use the underlying `netboot` program's 49 // default. 50 // `debug_on_failure` is an optional boolean that will signal a request for 51 // a debugging attempt if netboot fails. 52 // 53 // An example configuration is: 54 // { 55 // "type": "netboot", 56 // "method": "dhcpv6", 57 // "mac": "aa:bb:cc:dd:ee:ff", 58 // "override_url": "http://[fe80::face:booc]:8080/path/to/boot/file" 59 // } 60 // 61 // Note that the optional `override_url` in the example above will override 62 // whatever URL is returned in the OPT_BOOTFILE_URL for DHCPv6, or TFTP server 63 // name + bootfile URL in case of DHCPv4. 64 // 65 // Additional options may be added in the future. 66 log.Printf("Trying NetBooter...") 67 log.Printf("Config: %s", string(config)) 68 nb := NetBooter{} 69 if err := json.Unmarshal(config, &nb); err != nil { 70 return nil, err 71 } 72 log.Printf("NetBooter: %+v", nb) 73 if nb.Type != "netboot" { 74 return nil, fmt.Errorf("wrong type for NetBooter: %s", nb.Type) 75 } 76 return &nb, nil 77 } 78 79 // Boot will run the boot procedure. In the case of NetBooter, it will call the 80 // `netboot` command 81 func (nb *NetBooter) Boot() error { 82 bootcmd := []string{"netboot", "-d", "-userclass", "linuxboot"} 83 if nb.OverrideURL != nil { 84 bootcmd = append(bootcmd, "-netboot-url", *nb.OverrideURL) 85 } 86 if nb.Retries != nil { 87 bootcmd = append(bootcmd, "-retries", strconv.Itoa(*nb.Retries)) 88 } 89 if nb.Method == "dhcpv6" { 90 bootcmd = append(bootcmd, []string{"-6=true", "-4=false"}...) 91 } else if nb.Method == "dhcpv4" { 92 bootcmd = append(bootcmd, []string{"-6=false", "-4=true"}...) 93 } else { 94 return fmt.Errorf("netboot: unknown method %s", nb.Method) 95 } 96 if nb.DebugOnFailure { 97 bootcmd = append(bootcmd, "-fix") 98 } 99 log.Printf("Executing command: %v", bootcmd) 100 cmd := exec.Command(bootcmd[0], bootcmd[1:]...) 101 cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr 102 if err := cmd.Run(); err != nil { 103 return fmt.Errorf("error executing %v: %v", cmd, err) 104 } 105 return nil 106 } 107 108 // TypeName returns the name of the booter type 109 func (nb *NetBooter) TypeName() string { 110 return nb.Type 111 }