github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/tools/vpdbootmanager/add.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  	"encoding/json"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"net"
    13  	"os"
    14  
    15  	"github.com/u-root/u-root/pkg/boot/systembooter"
    16  	"github.com/u-root/u-root/pkg/vpd"
    17  )
    18  
    19  var dryRun = false
    20  
    21  func add(entrytype string, args []string) error {
    22  	var entry systembooter.Booter
    23  	var err error
    24  	switch entrytype {
    25  	case "netboot":
    26  		if len(args) < 2 {
    27  			return fmt.Errorf("You need to pass method and MAC address")
    28  		}
    29  		entry, err = parseNetbootFlags(args[0], args[1], args[2:])
    30  		if err != nil {
    31  			return err
    32  		}
    33  	case "localboot":
    34  		if len(args) < 1 {
    35  			return fmt.Errorf("You need to provide method")
    36  		}
    37  		entry, err = parseLocalbootFlags(args[0], args[1:])
    38  		if err != nil {
    39  			return err
    40  		}
    41  	default:
    42  		return fmt.Errorf("Unknown entry type")
    43  	}
    44  	if dryRun {
    45  		b, err := json.Marshal(entry)
    46  		if err != nil {
    47  			return err
    48  		}
    49  		fmt.Fprintln(os.Stderr, "Using -dryrun, will not write any variable. Content of boot entry:")
    50  		fmt.Println(string(b))
    51  		return nil
    52  	}
    53  	return addBootEntry(entry)
    54  }
    55  
    56  func parseLocalbootFlags(method string, args []string) (*systembooter.LocalBooter, error) {
    57  	cfg := &systembooter.LocalBooter{
    58  		Type:   "localboot",
    59  		Method: method,
    60  	}
    61  	flg := flag.NewFlagSet("localboot", flag.ExitOnError)
    62  	flg.StringVar(&cfg.KernelArgs, "kernel-args", "", "additional kernel args")
    63  	flg.StringVar(&cfg.Initramfs, "ramfs", "", "path of ramfs to be used for kexec'ing into the target kernel.")
    64  	flg.StringVar(&vpd.VpdDir, "vpd-dir", vpd.VpdDir, "VPD dir to use")
    65  	flg.BoolVar(&dryRun, "dryrun", false, "only print values that would be set")
    66  
    67  	switch method {
    68  	case "grub":
    69  		flg.Parse(args)
    70  	case "path":
    71  		if len(args) < 2 {
    72  			return nil, fmt.Errorf("You need to pass DeviceGUID and Kernel path")
    73  		}
    74  		cfg.DeviceGUID = args[0]
    75  		cfg.Kernel = args[1]
    76  		flg.Parse(args[2:])
    77  	default:
    78  		return nil, fmt.Errorf("Method needs to be grub or path")
    79  	}
    80  	return cfg, nil
    81  }
    82  
    83  func parseNetbootFlags(method, mac string, args []string) (*systembooter.NetBooter, error) {
    84  	if method != "dhcpv4" && method != "dhcpv6" {
    85  		return nil, fmt.Errorf("Method needs to be either dhcpv4 or dhcpv6")
    86  	}
    87  
    88  	_, err := net.ParseMAC(mac)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	cfg := &systembooter.NetBooter{
    94  		Type:   "netboot",
    95  		Method: method,
    96  		MAC:    mac,
    97  	}
    98  
    99  	flg := flag.NewFlagSet("netboot", flag.ExitOnError)
   100  	overrideURL := flg.String("override-url", "", "an optional URL used to override the boot file URL used")
   101  	retries := flg.Int("retries", -1, "the number of times a DHCP request should be retried if failed.")
   102  	flg.BoolVar(&dryRun, "dryrun", false, "only print values that would be set")
   103  	flg.StringVar(&vpd.VpdDir, "vpd-dir", vpd.VpdDir, "VPD dir to use")
   104  	flg.Parse(args)
   105  
   106  	if *overrideURL != "" {
   107  		cfg.OverrideURL = overrideURL
   108  	}
   109  
   110  	if *retries != -1 {
   111  		cfg.Retries = retries
   112  	}
   113  
   114  	return cfg, nil
   115  }
   116  
   117  func addBootEntry(cfg systembooter.Booter) error {
   118  	data, err := json.Marshal(cfg)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	for i := 1; i < vpd.MaxBootEntry; i++ {
   123  		key := fmt.Sprintf("Boot%04d", i)
   124  		if _, err := vpd.Get(key, false); err != nil {
   125  			if os.IsNotExist(err) {
   126  				if err := vpd.Set(key, data, false); err != nil {
   127  					return err
   128  				}
   129  				return nil
   130  			}
   131  			return err
   132  		}
   133  	}
   134  	return errors.New("Maximum number of boot entries already set")
   135  }