gopkg.in/hugelgupf/u-root.v2@v2.0.0-20180831055005-3f8fdb0ce09d/cmds/pxeboot/pxeboot.go (about)

     1  // Copyright 2017-2018 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 main
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"log"
    11  	"net/url"
    12  	"os"
    13  	"path"
    14  	"time"
    15  
    16  	"github.com/u-root/dhcp4/dhcp4client"
    17  	"github.com/u-root/u-root/pkg/dhclient"
    18  	"github.com/u-root/u-root/pkg/pxe"
    19  	"github.com/vishvananda/netlink"
    20  )
    21  
    22  var (
    23  	verbose = flag.Bool("v", true, "print all kinds of things out, more than Chris wants")
    24  	dryRun  = flag.Bool("dry-run", false, "download kernel, but don't kexec it")
    25  	debug   = func(string, ...interface{}) {}
    26  )
    27  
    28  func attemptDHCPLease(iface netlink.Link, timeout time.Duration, retry int) (*dhclient.Packet4, error) {
    29  	if _, err := dhclient.IfUp(iface.Attrs().Name); err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	client, err := dhcp4client.New(iface,
    34  		dhcp4client.WithTimeout(timeout),
    35  		dhcp4client.WithRetry(retry))
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	p, err := client.Request()
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	return dhclient.NewPacket4(p), nil
    45  }
    46  
    47  func Netboot() error {
    48  	ifs, err := netlink.LinkList()
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	for _, iface := range ifs {
    54  		// TODO: Do 'em all in parallel.
    55  		if iface.Attrs().Name != "eth0" {
    56  			continue
    57  		}
    58  
    59  		log.Printf("Attempting to get DHCP lease on %s", iface.Attrs().Name)
    60  		packet, err := attemptDHCPLease(iface, 30*time.Second, 5)
    61  		if err != nil {
    62  			log.Printf("No lease on %s: %v", iface.Attrs().Name, err)
    63  			continue
    64  		}
    65  		log.Printf("Got lease on %s", iface.Attrs().Name)
    66  		if err := dhclient.Configure4(iface, packet.P); err != nil {
    67  			log.Printf("shit: %v", err)
    68  			continue
    69  		}
    70  
    71  		// We may have to make this DHCPv6 and DHCPv4-specific anyway.
    72  		// Only tested with v4 right now; and assuming the uri points
    73  		// to a pxelinux.0.
    74  		//
    75  		// Or rather, we need to make this option-specific. DHCPv6 has
    76  		// options for passing a kernel and cmdline directly. v4
    77  		// usually just passes a pxelinux.0. But what about an initrd?
    78  		uri, err := packet.Boot()
    79  		if err != nil {
    80  			log.Printf("Got DHCP lease, but no valid PXE information.")
    81  			continue
    82  		}
    83  
    84  		log.Printf("Boot URI: %v", uri)
    85  
    86  		wd := &url.URL{
    87  			Scheme: uri.Scheme,
    88  			Host:   uri.Host,
    89  			Path:   path.Dir(uri.Path),
    90  		}
    91  		pc := pxe.NewConfig(wd)
    92  		if err := pc.FindConfigFile(iface.Attrs().HardwareAddr, packet.Lease().IP); err != nil {
    93  			return fmt.Errorf("failed to parse pxelinux config: %v", err)
    94  		}
    95  
    96  		label := pc.Entries[pc.DefaultEntry]
    97  		log.Printf("Got configuration: %v", label)
    98  
    99  		if *dryRun {
   100  			label.ExecutionInfo(log.New(os.Stderr, "", log.LstdFlags))
   101  		} else if err := label.Execute(); err != nil {
   102  			log.Printf("Kexec error: %v", err)
   103  		}
   104  	}
   105  	return nil
   106  }
   107  
   108  func main() {
   109  	flag.Parse()
   110  	if *verbose {
   111  		debug = log.Printf
   112  	}
   113  
   114  	if err := Netboot(); err != nil {
   115  		log.Fatal(err)
   116  	}
   117  }