github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/systembooter/localbooter.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 systembooter 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "log" 11 "os" 12 "os/exec" 13 ) 14 15 // LocalBooter implements the Booter interface for booting from local storage. 16 type LocalBooter struct { 17 Type string `json:"type"` 18 Method string `json:"method"` 19 DeviceGUID string `json:"device_guid"` 20 Kernel string `json:"kernel,omitempty"` 21 KernelArgs string `json:"kernel_args,omitempty"` 22 Initramfs string `json:"ramfs,omitempty"` 23 } 24 25 // NewLocalBooter parses a boot entry config and returns a Booter instance, or 26 // an error if any 27 func NewLocalBooter(config []byte) (Booter, error) { 28 /* 29 The configuration format for a LocalBooter entry is a JSON with the following structure: 30 31 { 32 "type": "localboot", 33 "method": "<method>", 34 "device_guid": "<device GUID or empty>" 35 "kernel": "<kernel path or empty>", 36 "kernel_args": "<kernel args or empty>", 37 "ramfs": "<ramfs path or empty>", 38 } 39 40 `type` is always set to "localboot" 41 `method` can be either "grub" or "path". 42 The "grub" method will look for grub.cfg or grub2.cfg on the specified device. 43 If no device is specified, it will look on all the attached storage devices, 44 sorted alphabetically as found in /dev. The first grub configuration that is 45 found is parsed, and kernel, kernel args and ramfs are extracted. Then the 46 kernel will be kexec'ed. If this fails, the next entry will NOT be tried, 47 and no other grub configs will be scanned. In case a grub config has no 48 valid boot entries, it is ignored and the next config will be used tried. 49 The "path" method requires a device GUID and kernel path to be specified. If 50 specified, it will also use kernel args and ramfs path. This method will look 51 for the given kernel on the given device, and will kexec the kernel using the 52 given, optional, kernel args and ramfs. 53 `device_guid` is the GUID of the device to look for grub config or kernel and ramfs 54 `kernel` is the path, relative to the device specified by `device_guid`, of the 55 kernel to be kexec'ed 56 `kernel_args` is the optional string of kernel arguments to be passed. 57 `ramfs` is the path, relative to the device specified by `device_guid`, of the ramfs 58 to be used for kexec'ing into the target kernel. 59 */ 60 log.Printf("Trying LocalBooter...") 61 log.Printf("Config: %s", string(config)) 62 lb := LocalBooter{} 63 if err := json.Unmarshal(config, &lb); err != nil { 64 return nil, err 65 } 66 log.Printf("LocalBooter: %+v", lb) 67 if lb.Type != "localboot" { 68 return nil, fmt.Errorf("wrong type for LocalBooter: %s", lb.Type) 69 } 70 // the actual arguments validation is done in `Boot` to avoid duplicate code 71 return &lb, nil 72 } 73 74 // Boot will run the boot procedure. In the case of LocalBooter, it will call 75 // the `localboot` command 76 func (lb *LocalBooter) Boot() error { 77 bootcmd := []string{"localboot", "-d"} 78 // validate arguments 79 if lb.Method == "grub" { 80 bootcmd = append(bootcmd, "-grub") 81 } else if lb.Method == "path" { 82 bootcmd = append(bootcmd, []string{"-kernel", lb.Kernel}...) 83 bootcmd = append(bootcmd, []string{"-guid", lb.DeviceGUID}...) 84 if lb.Initramfs != "" { 85 bootcmd = append(bootcmd, []string{"-initramfs", lb.Initramfs}...) 86 } 87 if lb.KernelArgs != "" { 88 bootcmd = append(bootcmd, []string{"-cmdline", lb.KernelArgs}...) 89 } 90 } else { 91 return fmt.Errorf("unknown boot method %s", lb.Method) 92 } 93 94 log.Printf("Executing command: %v", bootcmd) 95 cmd := exec.Command(bootcmd[0], bootcmd[1:]...) 96 cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr 97 if err := cmd.Run(); err != nil { 98 log.Printf("Error executing %v: %v", cmd, err) 99 } 100 return nil 101 } 102 103 // TypeName returns the name of the booter type 104 func (lb *LocalBooter) TypeName() string { 105 return lb.Type 106 }