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  }