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 }