gopkg.in/hugelgupf/u-root.v7@v7.0.0-20180831062900-6a07824681b2/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 }