github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+incompatible/xcmds/esxiboot/esxiboot.go (about)

     1  // Copyright 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  // esxiboot executes ESXi kernel over the running kernel.
     6  //
     7  // Synopsis:
     8  //     esxiboot --config <config> [-d (--device)]
     9  //
    10  // Description:
    11  //     Loads and executes ESXi kernel.
    12  //
    13  // Options:
    14  //     --device=FILE or -d=FILE: set the ESXi boot device
    15  //     --config=FILE or -c=FILE: set the ESXi config
    16  //
    17  // --device is required to kexec installed ESXi instance.
    18  // You don't need it if you kexec ESXi installer.
    19  //
    20  // The config file has the following syntax:
    21  //
    22  // kernel=PATH
    23  // kernelopt=OPTS
    24  // modules=MOD1 [ARGS] --- MOD2 [ARGS] --- ...
    25  //
    26  // Lines starting with '#' are ignored.
    27  
    28  package main
    29  
    30  import (
    31  	"bufio"
    32  	"encoding/hex"
    33  	"fmt"
    34  	"io"
    35  	"log"
    36  	"os"
    37  	"path/filepath"
    38  	"strings"
    39  
    40  	flag "github.com/spf13/pflag"
    41  
    42  	"github.com/u-root/u-root/pkg/gpt"
    43  	"github.com/u-root/u-root/pkg/kexec"
    44  	"github.com/u-root/u-root/pkg/multiboot"
    45  )
    46  
    47  var cfg = flag.StringP("config", "c", "", "Set the ESXi config")
    48  var dev = flag.StringP("device", "d", "", "Set the ESXi boot device")
    49  
    50  const (
    51  	kernel  = "kernel"
    52  	args    = "kernelopt"
    53  	modules = "modules"
    54  
    55  	comment = '#'
    56  	sep     = "---"
    57  
    58  	uuidMagic = "VMWARE FAT16    "
    59  	uuidSize  = 32
    60  	partition = 5
    61  )
    62  
    63  type options struct {
    64  	kernel  string
    65  	args    string
    66  	modules []string
    67  }
    68  
    69  func getUUID(device string) (string, error) {
    70  	device = strings.TrimRight(device, "/")
    71  	blockSize, err := gpt.GetBlockSize(device)
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  
    76  	f, err := os.Open(fmt.Sprintf("%s%d", device, partition))
    77  	if err != nil {
    78  		return "", err
    79  	}
    80  
    81  	// Boot uuid is stored in the second block of the disk
    82  	// in the following format:
    83  	//
    84  	// VMWARE FAT16    <uuid>
    85  	// <---128 bit----><128 bit>
    86  	data := make([]byte, uuidSize)
    87  	n, err := f.ReadAt(data, int64(blockSize))
    88  	if err != nil {
    89  		return "", err
    90  	}
    91  	if n != uuidSize {
    92  		return "", io.ErrUnexpectedEOF
    93  	}
    94  
    95  	if magic := string(data[:len(uuidMagic)]); magic != uuidMagic {
    96  		return "", fmt.Errorf("bad uuid magic %q", magic)
    97  	}
    98  
    99  	uuid := hex.EncodeToString(data[len(uuidMagic):])
   100  	return fmt.Sprintf("bootUUID=%s", uuid), nil
   101  }
   102  
   103  func (o *options) addUUID(device string) error {
   104  	uuid, err := getUUID(device)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	o.args += " " + uuid
   109  	return nil
   110  }
   111  
   112  func parse(fname string) (options, error) {
   113  	f, err := os.Open(fname)
   114  	if err != nil {
   115  		log.Fatal(err)
   116  	}
   117  	defer f.Close()
   118  
   119  	var opt options
   120  
   121  	scanner := bufio.NewScanner(f)
   122  	for scanner.Scan() {
   123  		line := scanner.Text()
   124  		line = strings.TrimSpace(line)
   125  
   126  		if len(line) == 0 || line[0] == comment {
   127  			continue
   128  		}
   129  
   130  		tokens := strings.SplitN(line, "=", 2)
   131  		if len(tokens) != 2 {
   132  			return opt, fmt.Errorf("bad line %q", line)
   133  		}
   134  		key := strings.TrimSpace(tokens[0])
   135  		val := strings.TrimSpace(tokens[1])
   136  		switch key {
   137  		case kernel:
   138  			opt.kernel = val
   139  		case args:
   140  			opt.args = val
   141  		case modules:
   142  			for _, tok := range strings.Split(val, sep) {
   143  				tok = strings.TrimSpace(tok)
   144  				opt.modules = append(opt.modules, tok)
   145  			}
   146  		}
   147  	}
   148  
   149  	err = scanner.Err()
   150  	return opt, err
   151  }
   152  
   153  func main() {
   154  	flag.Parse()
   155  	if *cfg == "" {
   156  		log.Fatalf("Config cannot be empty")
   157  	}
   158  
   159  	opts, err := parse(*cfg)
   160  	if err != nil {
   161  		log.Fatalf("Cannot parse config %v: %v", *cfg, err)
   162  	}
   163  
   164  	if *dev != "" {
   165  		if err := opts.addUUID(*dev); err != nil {
   166  			log.Fatalf("Cannot add boot uuid: %v", err)
   167  		}
   168  	}
   169  
   170  	p, err := os.Executable()
   171  	if err != nil {
   172  		log.Fatalf("Cannot find current executable path: %v", err)
   173  	}
   174  	trampoline, err := filepath.EvalSymlinks(p)
   175  	if err != nil {
   176  		log.Fatalf("Cannot eval symlinks for %v: %v", p, err)
   177  	}
   178  
   179  	m := multiboot.New(opts.kernel, opts.args, trampoline, opts.modules)
   180  	if err := m.Load(false); err != nil {
   181  		log.Fatalf("Load failed: %v", err)
   182  	}
   183  
   184  	if err := kexec.Load(m.EntryPoint, m.Segments(), 0); err != nil {
   185  		log.Fatalf("kexec.Load() error: %v", err)
   186  	}
   187  	if err := kexec.Reboot(); err != nil {
   188  		log.Fatalf("kexec.Reboot() error: %v", err)
   189  	}
   190  }