github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/boot/systemboot/main.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 main
     6  
     7  import (
     8  	"flag"
     9  	"log"
    10  	"os"
    11  	"os/exec"
    12  	"os/signal"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/u-root/u-root/pkg/booter"
    17  	"github.com/u-root/u-root/pkg/ipmi"
    18  	"github.com/u-root/u-root/pkg/smbios"
    19  )
    20  
    21  var (
    22  	allowInteractive = flag.Bool("i", true, "Allow user to interrupt boot process and run commands")
    23  	doQuiet          = flag.Bool("q", false, "Disable verbose output")
    24  	interval         = flag.Int("I", 1, "Interval in seconds before looping to the next boot command")
    25  	noDefaultBoot    = flag.Bool("nodefault", false, "Do not attempt default boot entries if regular ones fail")
    26  )
    27  
    28  var defaultBootsequence = [][]string{
    29  	{"fbnetboot", "-userclass", "linuxboot"},
    30  	{"localboot", "-grub"},
    31  }
    32  
    33  // Product list for running IPMI OEM commands
    34  var productList = [2]string{"Tioga Pass", "Mono Lake"}
    35  
    36  func isMatched(productName string) bool {
    37  	for _, v := range productList {
    38  		if strings.HasPrefix(productName, v) {
    39  			return true
    40  		}
    41  	}
    42  	return false
    43  }
    44  
    45  func getSystemProductName(si *smbios.Info) (string, error) {
    46  	t1, err := si.GetSystemInfo()
    47  	if err != nil {
    48  		log.Printf("Error getting System Information: %v", err)
    49  		return "", err
    50  	}
    51  	return t1.ProductName, nil
    52  }
    53  
    54  func getSystemFWVersion(si *smbios.Info) (string, error) {
    55  	t0, err := si.GetBIOSInfo()
    56  	if err != nil {
    57  		log.Printf("Error getting BIOS Information: %v", err)
    58  		return "", err
    59  	}
    60  	return t0.Version, nil
    61  }
    62  
    63  func runIPMICommands() {
    64  	ipmi, err := ipmi.Open(0)
    65  	if err != nil {
    66  		log.Printf("Failed to open ipmi device %v, watchdog may still be running", err)
    67  		return
    68  	}
    69  	defer ipmi.Close()
    70  
    71  	if err = ipmi.ShutoffWatchdog(); err != nil {
    72  		log.Printf("Failed to stop watchdog %v.", err)
    73  	} else {
    74  		log.Printf("Watchdog is stopped.")
    75  	}
    76  
    77  	// Below IPMI commands would require SMBIOS data
    78  	si, err := smbios.FromSysfs()
    79  	if err != nil {
    80  		log.Printf("Error reading SMBIOS info: %v", err)
    81  		return
    82  	}
    83  
    84  	if fwVersion, err := getSystemFWVersion(si); err == nil {
    85  		log.Printf("System firmware version: %s", fwVersion)
    86  		if err = ipmi.SetSystemFWVersion(fwVersion); err != nil {
    87  			log.Printf("Failed to set system firmware version to BMC %v.", err)
    88  		}
    89  	}
    90  
    91  	if productName, err := getSystemProductName(si); err == nil {
    92  		if isMatched(productName) {
    93  			log.Printf("Running OEM IPMI commands.")
    94  		} else {
    95  			log.Printf("No product name is matched for OEM commands.")
    96  		}
    97  	}
    98  
    99  }
   100  
   101  func main() {
   102  	flag.Parse()
   103  
   104  	log.Print(`
   105                       ____            _                 _                 _   
   106                      / ___| _   _ ___| |_ ___ _ __ ___ | |__   ___   ___ | |_ 
   107                      \___ \| | | / __| __/ _ \ '_ ` + "`" + ` _ \| '_ \ / _ \ / _ \| __|
   108                       ___) | |_| \__ \ ||  __/ | | | | | |_) | (_) | (_) | |_ 
   109                      |____/ \__, |___/\__\___|_| |_| |_|_.__/ \___/ \___/ \__|
   110                             |___/
   111  `)
   112  	runIPMICommands()
   113  	sleepInterval := time.Duration(*interval) * time.Second
   114  	if *allowInteractive {
   115  		log.Printf("**************************************************************************")
   116  		log.Print("Starting boot sequence, press CTRL-C within 5 seconds to drop into a shell")
   117  		log.Printf("**************************************************************************")
   118  		time.Sleep(5 * time.Second)
   119  	} else {
   120  		signal.Ignore()
   121  	}
   122  
   123  	// Get and show boot entries
   124  	bootEntries := booter.GetBootEntries()
   125  	log.Printf("BOOT ENTRIES:")
   126  	for _, entry := range bootEntries {
   127  		log.Printf("    %v) %+v", entry.Name, string(entry.Config))
   128  	}
   129  	for _, entry := range bootEntries {
   130  		log.Printf("Trying boot entry %s: %s", entry.Name, string(entry.Config))
   131  		if err := entry.Booter.Boot(); err != nil {
   132  			log.Printf("Warning: failed to boot with configuration: %+v", entry)
   133  		}
   134  		if !*doQuiet {
   135  			log.Printf("Sleeping %v before attempting next boot command", sleepInterval)
   136  		}
   137  		time.Sleep(sleepInterval)
   138  	}
   139  
   140  	// if boot entries failed, use the default boot sequence
   141  	log.Printf("Boot entries failed")
   142  
   143  	if !*noDefaultBoot {
   144  		log.Print("Falling back to the default boot sequence")
   145  		for {
   146  			for _, bootcmd := range defaultBootsequence {
   147  				if !*doQuiet {
   148  					bootcmd = append(bootcmd, "-d")
   149  				}
   150  				log.Printf("Running boot command: %v", bootcmd)
   151  				cmd := exec.Command(bootcmd[0], bootcmd[1:]...)
   152  				cmd.Stdout = os.Stdout
   153  				cmd.Stderr = os.Stderr
   154  				if err := cmd.Run(); err != nil {
   155  					log.Printf("Error executing %v: %v", cmd, err)
   156  				}
   157  			}
   158  			if !*doQuiet {
   159  				log.Printf("Sleeping %v before attempting next boot command", sleepInterval)
   160  			}
   161  			time.Sleep(sleepInterval)
   162  		}
   163  	}
   164  }