github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/boot/initramfs.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package boot 21 22 import ( 23 "os/exec" 24 "time" 25 26 "github.com/snapcore/snapd/bootloader" 27 "github.com/snapcore/snapd/osutil" 28 "github.com/snapcore/snapd/snap" 29 ) 30 31 // InitramfsRunModeSelectSnapsToMount returns a map of the snap paths to mount 32 // for the specified snap types. 33 func InitramfsRunModeSelectSnapsToMount( 34 typs []snap.Type, 35 modeenv *Modeenv, 36 ) (map[snap.Type]snap.PlaceInfo, error) { 37 var sn snap.PlaceInfo 38 var err error 39 m := make(map[snap.Type]snap.PlaceInfo) 40 for _, typ := range typs { 41 // TODO: consider passing a bootStateUpdate20 instead? 42 var selectSnapFn func(*Modeenv) (snap.PlaceInfo, error) 43 switch typ { 44 case snap.TypeBase: 45 bs := &bootState20Base{} 46 selectSnapFn = bs.selectAndCommitSnapInitramfsMount 47 case snap.TypeKernel: 48 blOpts := &bootloader.Options{ 49 Role: bootloader.RoleRunMode, 50 NoSlashBoot: true, 51 } 52 blDir := InitramfsUbuntuBootDir 53 bs := &bootState20Kernel{ 54 blDir: blDir, 55 blOpts: blOpts, 56 } 57 selectSnapFn = bs.selectAndCommitSnapInitramfsMount 58 } 59 sn, err = selectSnapFn(modeenv) 60 if err != nil { 61 return nil, err 62 } 63 64 m[typ] = sn 65 } 66 67 return m, nil 68 } 69 70 // EnsureNextBootToRunMode will mark the bootenv of the recovery bootloader such 71 // that recover mode is now ready to switch back to run mode upon any reboot. 72 func EnsureNextBootToRunMode(systemLabel string) error { 73 // at the end of the initramfs we need to set the bootenv such that a reboot 74 // now at any point will rollback to run mode without additional config or 75 // actions 76 77 opts := &bootloader.Options{ 78 // setup the recovery bootloader 79 Role: bootloader.RoleRecovery, 80 } 81 82 bl, err := bootloader.Find(InitramfsUbuntuSeedDir, opts) 83 if err != nil { 84 return err 85 } 86 87 m := map[string]string{ 88 "snapd_recovery_system": systemLabel, 89 "snapd_recovery_mode": "run", 90 } 91 return bl.SetBootVars(m) 92 } 93 94 // initramfsReboot triggers a reboot from the initramfs immediately 95 var initramfsReboot = func() error { 96 if osutil.IsTestBinary() { 97 panic("initramfsReboot must be mocked in tests") 98 } 99 100 out, err := exec.Command("/sbin/reboot").CombinedOutput() 101 if err != nil { 102 return osutil.OutputErr(out, err) 103 } 104 105 // reboot command in practice seems to not return, but apparently it is 106 // theoretically possible it could return, so to account for this we will 107 // loop for a "long" time waiting for the system to be rebooted, and panic 108 // after a timeout so that if something goes wrong with the reboot we do 109 // exit with some info about the expected reboot 110 time.Sleep(10 * time.Minute) 111 panic("expected reboot to happen within 10 minutes after calling /sbin/reboot") 112 } 113 114 func MockInitramfsReboot(f func() error) (restore func()) { 115 osutil.MustBeTestBinary("initramfsReboot only can be mocked in tests") 116 old := initramfsReboot 117 initramfsReboot = f 118 return func() { 119 initramfsReboot = old 120 } 121 }